mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
241 lines
7.4 KiB
TypeScript
241 lines
7.4 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import {
|
|
Stack,
|
|
Title,
|
|
Group,
|
|
Button,
|
|
Paper,
|
|
Text,
|
|
Grid,
|
|
Modal,
|
|
ActionIcon,
|
|
Tooltip,
|
|
Loader,
|
|
Center,
|
|
Alert,
|
|
} from '@mantine/core';
|
|
import { useDisclosure } from '@mantine/hooks';
|
|
import { IconSettings, IconMap, IconInfoCircle, IconPlus } from '@tabler/icons-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { notify } from '@ema-platform/ui';
|
|
import { LocationTree } from '../components/LocationTree';
|
|
import { LocationDetail } from '../components/LocationDetail';
|
|
import { LocationForm } from '../components/LocationForm';
|
|
import { LocationTypeModal } from '../components/LocationTypeModal';
|
|
import {
|
|
useGetLocationTypesQuery,
|
|
useCreateLocationMutation,
|
|
useUpdateLocationMutation,
|
|
useDeleteLocationMutation,
|
|
} from '../api/location-api';
|
|
import type { Location } from '../types/location';
|
|
|
|
export function LocationPage() {
|
|
const { t } = useTranslation();
|
|
const { data: locationTypesRes, isLoading: typesLoading } = useGetLocationTypesQuery();
|
|
const [createLocation, { isLoading: isCreating }] = useCreateLocationMutation();
|
|
const [updateLocation, { isLoading: isUpdating }] = useUpdateLocationMutation();
|
|
const [deleteLocation] = useDeleteLocationMutation();
|
|
|
|
const locationTypes = locationTypesRes?.items ?? [];
|
|
|
|
const [selectedLocation, setSelectedLocation] = useState<Location | null>(null);
|
|
const [typeModalOpened, { open: openTypeModal, close: closeTypeModal }] = useDisclosure(false);
|
|
const [formModalOpened, { open: openFormModal, close: closeFormModal }] = useDisclosure(false);
|
|
const [deleteModalOpened, { open: openDeleteModal, close: closeDeleteModal }] = useDisclosure(false);
|
|
const [editingLocation, setEditingLocation] = useState<Location | null>(null);
|
|
const [parentLocation, setParentLocation] = useState<Location | null>(null);
|
|
|
|
const handleSelect = useCallback((loc: Location) => {
|
|
setSelectedLocation(loc);
|
|
}, []);
|
|
|
|
const handleAddChild = useCallback(() => {
|
|
setEditingLocation(null);
|
|
setParentLocation(selectedLocation);
|
|
openFormModal();
|
|
}, [selectedLocation, openFormModal]);
|
|
|
|
const handleEdit = useCallback(() => {
|
|
if (!selectedLocation) return;
|
|
setEditingLocation(selectedLocation);
|
|
setParentLocation(null);
|
|
openFormModal();
|
|
}, [selectedLocation, openFormModal]);
|
|
|
|
const handleFormSubmit = useCallback(
|
|
async (values: {
|
|
code: string;
|
|
names: { en: string; am: string };
|
|
locationTypeId: string;
|
|
parentId?: string | null;
|
|
}) => {
|
|
try {
|
|
if (editingLocation) {
|
|
await updateLocation({ id: editingLocation.id, ...values }).unwrap();
|
|
notify.success(t('location.updated'));
|
|
} else {
|
|
await createLocation(values).unwrap();
|
|
notify.success(t('location.created'));
|
|
}
|
|
closeFormModal();
|
|
setEditingLocation(null);
|
|
setParentLocation(null);
|
|
} catch {
|
|
notify.error(t('location.error'));
|
|
}
|
|
},
|
|
[editingLocation, createLocation, updateLocation, closeFormModal, t],
|
|
);
|
|
|
|
const handleDeleteConfirm = useCallback(async () => {
|
|
if (!selectedLocation) return;
|
|
try {
|
|
await deleteLocation(selectedLocation.id).unwrap();
|
|
notify.success(t('location.deleted'));
|
|
setSelectedLocation(null);
|
|
closeDeleteModal();
|
|
} catch {
|
|
notify.error(t('location.deleteError'));
|
|
}
|
|
}, [selectedLocation, deleteLocation, closeDeleteModal, t]);
|
|
|
|
if (typesLoading) {
|
|
return (
|
|
<Center py="xl">
|
|
<Loader />
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack gap="lg">
|
|
<Group justify="space-between">
|
|
<Title order={2}>{t('location.title')}</Title>
|
|
<Group gap="sm">
|
|
{locationTypes.length > 0 && (
|
|
<Button
|
|
variant="light"
|
|
leftSection={<IconPlus size={16} />}
|
|
onClick={() => {
|
|
setEditingLocation(null);
|
|
setParentLocation(null);
|
|
openFormModal();
|
|
}}
|
|
size="sm"
|
|
>
|
|
{t('location.addRoot')}
|
|
</Button>
|
|
)}
|
|
<Tooltip label={t('location.manageTypes')}>
|
|
<ActionIcon
|
|
variant="subtle"
|
|
color="gray"
|
|
size="lg"
|
|
onClick={openTypeModal}
|
|
>
|
|
<IconSettings size={20} />
|
|
</ActionIcon>
|
|
</Tooltip>
|
|
</Group>
|
|
</Group>
|
|
|
|
{locationTypes.length === 0 && (
|
|
<Alert
|
|
icon={<IconInfoCircle size={16} />}
|
|
title={t('location.setupRequired')}
|
|
color="blue"
|
|
>
|
|
<Text size="sm" mb="sm">
|
|
{t('location.setupHint')}
|
|
</Text>
|
|
<Button variant="light" size="sm" onClick={openTypeModal}>
|
|
{t('location.configureTypes')}
|
|
</Button>
|
|
</Alert>
|
|
)}
|
|
|
|
{locationTypes.length > 0 && (
|
|
<Grid gutter="md">
|
|
<Grid.Col span={{ base: 12, md: 5 }}>
|
|
<Paper p="md" radius="md" withBorder style={{ maxHeight: 'calc(100vh - 260px)', overflow: 'auto' }}>
|
|
<Text fw={600} size="sm" mb="sm">
|
|
{t('location.hierarchy')}
|
|
</Text>
|
|
<LocationTree
|
|
selectedId={selectedLocation?.id ?? null}
|
|
onSelect={handleSelect}
|
|
/>
|
|
</Paper>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, md: 7 }}>
|
|
{selectedLocation ? (
|
|
<LocationDetail
|
|
location={selectedLocation}
|
|
locationTypes={locationTypes}
|
|
onAddChild={handleAddChild}
|
|
onEdit={handleEdit}
|
|
onDelete={openDeleteModal}
|
|
/>
|
|
) : (
|
|
<Paper p="xl" radius="md" withBorder>
|
|
<Center py="xl">
|
|
<Stack align="center" gap="sm">
|
|
<IconMap size={48} stroke={1} color="var(--mantine-color-gray-4)" />
|
|
<Text c="dimmed" size="sm">
|
|
{t('location.selectHint')}
|
|
</Text>
|
|
</Stack>
|
|
</Center>
|
|
</Paper>
|
|
)}
|
|
</Grid.Col>
|
|
</Grid>
|
|
)}
|
|
|
|
<Modal
|
|
opened={formModalOpened}
|
|
onClose={closeFormModal}
|
|
title={editingLocation ? t('location.editTitle') : t('location.addTitle')}
|
|
size="md"
|
|
>
|
|
<LocationForm
|
|
locationTypes={locationTypes}
|
|
parentLocation={parentLocation}
|
|
editingLocation={editingLocation}
|
|
onSubmit={handleFormSubmit}
|
|
onCancel={() => {
|
|
closeFormModal();
|
|
setEditingLocation(null);
|
|
setParentLocation(null);
|
|
}}
|
|
isSubmitting={isCreating || isUpdating}
|
|
/>
|
|
</Modal>
|
|
|
|
<Modal
|
|
opened={deleteModalOpened}
|
|
onClose={closeDeleteModal}
|
|
title={t('location.confirmDelete')}
|
|
size="sm"
|
|
>
|
|
<Text mb="md">
|
|
{t('location.deleteConfirmText', {
|
|
name: selectedLocation?.names.en ?? '',
|
|
})}
|
|
</Text>
|
|
<Group justify="flex-end">
|
|
<Button variant="default" onClick={closeDeleteModal} size="sm">
|
|
{t('location.cancel')}
|
|
</Button>
|
|
<Button color="red" onClick={handleDeleteConfirm} size="sm">
|
|
{t('location.delete')}
|
|
</Button>
|
|
</Group>
|
|
</Modal>
|
|
|
|
<LocationTypeModal opened={typeModalOpened} onClose={closeTypeModal} />
|
|
</Stack>
|
|
);
|
|
}
|