Merge branch 'main' of ssh://85.143.175.152:222/dry_wash_inc/dry-wash-pl
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				it-academy/dry-wash-pl/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	it-academy/dry-wash-pl/pipeline/head This commit looks good
				
			This commit is contained in:
		
						commit
						e73773f359
					
				@ -73,6 +73,10 @@
 | 
				
			|||||||
  "dry-wash.arm.master.table.header.phone": "Phone",
 | 
					  "dry-wash.arm.master.table.header.phone": "Phone",
 | 
				
			||||||
  "dry-wash.arm.master.table.header.actions": "Actions",
 | 
					  "dry-wash.arm.master.table.header.actions": "Actions",
 | 
				
			||||||
  "dry-wash.arm.master.table.actionsMenu.delete": "Delete Master",
 | 
					  "dry-wash.arm.master.table.actionsMenu.delete": "Delete Master",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.schedule.empty": "free",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.cancel": "Undo changes",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.save": "Save changes ",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.edit": "Edit",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.title": "Add New Master",
 | 
					  "dry-wash.arm.master.drawer.title": "Add New Master",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.inputName.label": "Full Name",
 | 
					  "dry-wash.arm.master.drawer.inputName.label": "Full Name",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.inputName.placeholder": "Enter Full Name",
 | 
					  "dry-wash.arm.master.drawer.inputName.placeholder": "Enter Full Name",
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,10 @@
 | 
				
			|||||||
  "dry-wash.arm.master.table.header.phone": "Телефон",
 | 
					  "dry-wash.arm.master.table.header.phone": "Телефон",
 | 
				
			||||||
  "dry-wash.arm.master.table.header.actions": "Действия",
 | 
					  "dry-wash.arm.master.table.header.actions": "Действия",
 | 
				
			||||||
  "dry-wash.arm.master.table.actionsMenu.delete": "Удалить мастера",
 | 
					  "dry-wash.arm.master.table.actionsMenu.delete": "Удалить мастера",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.schedule.empty": "Свободен",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.cancel": "Отменить изменения",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.save": "Сохранить изменения",
 | 
				
			||||||
 | 
					  "dry-wash.arm.master.editable.aria.edit": "Редактировать",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.title": "Добавить нового мастера",
 | 
					  "dry-wash.arm.master.drawer.title": "Добавить нового мастера",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.inputName.label": "ФИО",
 | 
					  "dry-wash.arm.master.drawer.inputName.label": "ФИО",
 | 
				
			||||||
  "dry-wash.arm.master.drawer.inputName.placeholder": "Введите ФИО",
 | 
					  "dry-wash.arm.master.drawer.inputName.placeholder": "Введите ФИО",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
import { getConfigValue } from '@brojs/cli';
 | 
					import { getConfigValue } from '@brojs/cli';
 | 
				
			||||||
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ArmEndpoints {
 | 
					enum ArmEndpoints {
 | 
				
			||||||
  ORDERS = '/arm/orders',
 | 
					  ORDERS = '/arm/orders',
 | 
				
			||||||
@ -9,12 +10,15 @@ const armService = () => {
 | 
				
			|||||||
  const endpoint = getConfigValue('dry-wash.api');
 | 
					  const endpoint = getConfigValue('dry-wash.api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const fetchOrders = async ({ date }: { date: Date }) => {
 | 
					  const fetchOrders = async ({ date }: { date: Date }) => {
 | 
				
			||||||
 | 
					    const startDate = dayjs(date).startOf('day').toISOString();
 | 
				
			||||||
 | 
					    const endDate = dayjs(date).endOf('day').toISOString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const response = await fetch(`${endpoint}${ArmEndpoints.ORDERS}`, {
 | 
					    const response = await fetch(`${endpoint}${ArmEndpoints.ORDERS}`, {
 | 
				
			||||||
      method: 'POST',
 | 
					      method: 'POST',
 | 
				
			||||||
      headers: {
 | 
					      headers: {
 | 
				
			||||||
        'Content-Type': 'application/json',
 | 
					        'Content-Type': 'application/json',
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      body: JSON.stringify({ date }),
 | 
					      body: JSON.stringify({ startDate, endDate }),
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!response.ok) {
 | 
					    if (!response.ok) {
 | 
				
			||||||
@ -68,7 +72,33 @@ const armService = () => {
 | 
				
			|||||||
    return await response.json();
 | 
					    return await response.json();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return { fetchOrders, fetchMasters, addMaster, deleteMaster };
 | 
					  const updateMaster = async ({
 | 
				
			||||||
 | 
					    id,
 | 
				
			||||||
 | 
					    name,
 | 
				
			||||||
 | 
					    phone,
 | 
				
			||||||
 | 
					  }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    name?: string;
 | 
				
			||||||
 | 
					    phone?: string;
 | 
				
			||||||
 | 
					  }) => {
 | 
				
			||||||
 | 
					    const body = JSON.stringify({ name, phone });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const response = await fetch(`${endpoint}${ArmEndpoints.MASTERS}/${id}`, {
 | 
				
			||||||
 | 
					      method: 'PATCH',
 | 
				
			||||||
 | 
					      headers: {
 | 
				
			||||||
 | 
					        'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      body,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!response.ok) {
 | 
				
			||||||
 | 
					      throw new Error(`Failed to fetch update masters: ${response.status}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return await response.json();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { fetchOrders, fetchMasters, addMaster, deleteMaster, updateMaster };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { armService, ArmEndpoints };
 | 
					export { armService, ArmEndpoints };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										119
									
								
								src/components/Editable/Editable.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/components/Editable/Editable.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Editable,
 | 
				
			||||||
 | 
					  EditableInput,
 | 
				
			||||||
 | 
					  EditablePreview,
 | 
				
			||||||
 | 
					  Flex,
 | 
				
			||||||
 | 
					  IconButton,
 | 
				
			||||||
 | 
					  Input,
 | 
				
			||||||
 | 
					  useEditableControls,
 | 
				
			||||||
 | 
					  ButtonGroup,
 | 
				
			||||||
 | 
					  Stack,
 | 
				
			||||||
 | 
					  useToast,
 | 
				
			||||||
 | 
					} from '@chakra-ui/react';
 | 
				
			||||||
 | 
					import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface EditableWrapperProps {
 | 
				
			||||||
 | 
					  value: string;
 | 
				
			||||||
 | 
					  onSubmit: ({
 | 
				
			||||||
 | 
					    id,
 | 
				
			||||||
 | 
					    name,
 | 
				
			||||||
 | 
					    phone,
 | 
				
			||||||
 | 
					  }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    name?: string;
 | 
				
			||||||
 | 
					    phone?: string;
 | 
				
			||||||
 | 
					  }) => Promise<unknown>;
 | 
				
			||||||
 | 
					  as: 'phone' | 'name';
 | 
				
			||||||
 | 
					  id: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const EditableWrapper = ({ value, onSubmit, as, id }: EditableWrapperProps) => {
 | 
				
			||||||
 | 
					  const { t } = useTranslation('~', {
 | 
				
			||||||
 | 
					    keyPrefix: 'dry-wash.arm.master.editable',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const toast = useToast();
 | 
				
			||||||
 | 
					  const [currentValue, setCurrentValue] = useState<string>(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = async (newValue: string) => {
 | 
				
			||||||
 | 
					    if (currentValue === newValue) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await onSubmit({ id, [as]: newValue });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setCurrentValue(newValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      toast({
 | 
				
			||||||
 | 
					        title: 'Успешно!',
 | 
				
			||||||
 | 
					        description: 'Данные обновлены.',
 | 
				
			||||||
 | 
					        status: 'success',
 | 
				
			||||||
 | 
					        duration: 2000,
 | 
				
			||||||
 | 
					        isClosable: true,
 | 
				
			||||||
 | 
					        position: 'top-right',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      toast({
 | 
				
			||||||
 | 
					        title: 'Ошибка!',
 | 
				
			||||||
 | 
					        description: 'Не удалось обновить данные.',
 | 
				
			||||||
 | 
					        status: 'error',
 | 
				
			||||||
 | 
					        duration: 2000,
 | 
				
			||||||
 | 
					        isClosable: true,
 | 
				
			||||||
 | 
					        position: 'top-right',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      console.error('Ошибка при обновлении данных:', error);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function EditableControls() {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					      isEditing,
 | 
				
			||||||
 | 
					      getSubmitButtonProps,
 | 
				
			||||||
 | 
					      getCancelButtonProps,
 | 
				
			||||||
 | 
					      getEditButtonProps,
 | 
				
			||||||
 | 
					    } = useEditableControls();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return isEditing ? (
 | 
				
			||||||
 | 
					      <ButtonGroup justifyContent='center' size='sm'>
 | 
				
			||||||
 | 
					        <IconButton
 | 
				
			||||||
 | 
					          aria-label={t('aria.save')}
 | 
				
			||||||
 | 
					          icon={<CheckIcon />}
 | 
				
			||||||
 | 
					          {...getSubmitButtonProps()}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <IconButton
 | 
				
			||||||
 | 
					          aria-label={t('aria.cancel')}
 | 
				
			||||||
 | 
					          icon={<CloseIcon />}
 | 
				
			||||||
 | 
					          {...getCancelButtonProps()}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </ButtonGroup>
 | 
				
			||||||
 | 
					    ) : (
 | 
				
			||||||
 | 
					      <Flex justifyContent='center'>
 | 
				
			||||||
 | 
					        <IconButton
 | 
				
			||||||
 | 
					          aria-label={t('aria.edit')}
 | 
				
			||||||
 | 
					          size='sm'
 | 
				
			||||||
 | 
					          icon={<EditIcon />}
 | 
				
			||||||
 | 
					          {...getEditButtonProps()}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </Flex>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Editable
 | 
				
			||||||
 | 
					      textAlign='center'
 | 
				
			||||||
 | 
					      defaultValue={currentValue}
 | 
				
			||||||
 | 
					      fontSize='2xl'
 | 
				
			||||||
 | 
					      isPreviewFocusable={false}
 | 
				
			||||||
 | 
					      onSubmit={handleSubmit}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Stack direction={['column', 'row']} spacing='15px'>
 | 
				
			||||||
 | 
					        <EditablePreview />
 | 
				
			||||||
 | 
					        <Input as={EditableInput} />
 | 
				
			||||||
 | 
					        <EditableControls />
 | 
				
			||||||
 | 
					      </Stack>
 | 
				
			||||||
 | 
					    </Editable>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default EditableWrapper;
 | 
				
			||||||
@ -1,8 +1,11 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { Badge, Link, Stack, Td, Tr } from '@chakra-ui/react';
 | 
					import { Badge, Stack, Td, Tr, Text } from '@chakra-ui/react';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MasterActionsMenu from '../MasterActionsMenu';
 | 
					import MasterActionsMenu from '../MasterActionsMenu';
 | 
				
			||||||
import { getTimeSlot } from '../../lib';
 | 
					import { getTimeSlot } from '../../lib';
 | 
				
			||||||
 | 
					import EditableWrapper from '../Editable/Editable';
 | 
				
			||||||
 | 
					import { armService } from '../../api/arm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Schedule {
 | 
					export interface Schedule {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@ -18,20 +21,41 @@ export type MasterProps = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MasterItem = ({ name, phone, id, schedule }) => {
 | 
					const MasterItem = ({ name, phone, id, schedule }) => {
 | 
				
			||||||
 | 
					  const { updateMaster } = armService();
 | 
				
			||||||
 | 
					  const { t } = useTranslation('~', {
 | 
				
			||||||
 | 
					    keyPrefix: 'dry-wash.arm.master',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Tr>
 | 
					    <Tr>
 | 
				
			||||||
      <Td>{name}</Td>
 | 
					 | 
				
			||||||
      <Td>
 | 
					      <Td>
 | 
				
			||||||
        <Stack direction='row'>
 | 
					        <EditableWrapper
 | 
				
			||||||
          {schedule?.map(({ startWashTime, endWashTime }, index) => (
 | 
					          id={id}
 | 
				
			||||||
            <Badge colorScheme={'green'} key={index}>
 | 
					          as={'name'}
 | 
				
			||||||
              {getTimeSlot(startWashTime, endWashTime)}
 | 
					          value={name}
 | 
				
			||||||
            </Badge>
 | 
					          onSubmit={updateMaster}
 | 
				
			||||||
          ))}
 | 
					        />
 | 
				
			||||||
        </Stack>
 | 
					 | 
				
			||||||
      </Td>
 | 
					      </Td>
 | 
				
			||||||
      <Td>
 | 
					      <Td>
 | 
				
			||||||
        <Link href='tel:'>{phone}</Link>
 | 
					        {schedule?.length > 0 ? (
 | 
				
			||||||
 | 
					          <Stack direction='row'>
 | 
				
			||||||
 | 
					            {schedule?.map(({ startWashTime, endWashTime }, index: number) => (
 | 
				
			||||||
 | 
					              <Badge colorScheme={'green'} key={index}>
 | 
				
			||||||
 | 
					                {getTimeSlot(startWashTime, endWashTime)}
 | 
				
			||||||
 | 
					              </Badge>
 | 
				
			||||||
 | 
					            ))}
 | 
				
			||||||
 | 
					          </Stack>
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Text color='gray.500'>{t('schedule.empty')}</Text>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Td>
 | 
				
			||||||
 | 
					      <Td>
 | 
				
			||||||
 | 
					        <EditableWrapper
 | 
				
			||||||
 | 
					          id={id}
 | 
				
			||||||
 | 
					          as={'phone'}
 | 
				
			||||||
 | 
					          value={phone}
 | 
				
			||||||
 | 
					          onSubmit={updateMaster}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </Td>
 | 
					      </Td>
 | 
				
			||||||
      <Td>
 | 
					      <Td>
 | 
				
			||||||
        <MasterActionsMenu id={id} />
 | 
					        <MasterActionsMenu id={id} />
 | 
				
			||||||
 | 
				
			|||||||
@ -2,22 +2,12 @@
 | 
				
			|||||||
  "success": true,
 | 
					  "success": true,
 | 
				
			||||||
  "body": [
 | 
					  "body": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "id": "masters1",
 | 
					      "id": "4545423234",
 | 
				
			||||||
      "name": "Иван Иванов",
 | 
					      "name": "Иван Иванов",
 | 
				
			||||||
      "schedule": [ {
 | 
					 | 
				
			||||||
        "id": "order1",
 | 
					 | 
				
			||||||
        "startWashTime": "2024-11-24T10:30:00.000Z",
 | 
					 | 
				
			||||||
        "endWashTime": "2024-11-24T16:30:00.000Z"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "id": "order2",
 | 
					 | 
				
			||||||
          "startWashTime": "2024-11-24T11:30:00.000Z",
 | 
					 | 
				
			||||||
          "endWashTime": "2024-11-24T17:30:00.000Z"
 | 
					 | 
				
			||||||
        }],
 | 
					 | 
				
			||||||
      "phone": "+7 900 123 45 67"
 | 
					      "phone": "+7 900 123 45 67"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "id": "masters12",
 | 
					      "id": "345354234",
 | 
				
			||||||
      "name": "Иван Иванов",
 | 
					      "name": "Иван Иванов",
 | 
				
			||||||
      "schedule": [ {
 | 
					      "schedule": [ {
 | 
				
			||||||
        "id": "order1",
 | 
					        "id": "order1",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user