Compare commits

..

No commits in common. "c2fad8a3ff379972c8fad87c8eb2a45d08364189" and "a63304b5e4ebc7028ba40a49fa0332d0a8b2bc45" have entirely different histories.

12 changed files with 102 additions and 177 deletions
locales
src
components
ErrorBoundary
MasterActionsMenu
MasterDrawer
Masters
OrderItem
Orders
Sidebar
landing
CtaButton
SiteLogo
pages/arm

@ -13,41 +13,5 @@
"dry-wash.landing.hero-section.headline": "Revitalize Your Ride with Eco-Friendly Care!",
"dry-wash.landing.make-order-button": "Make order",
"dry-wash.landing.site-logo": "The logo of the \"Dry Master\" company",
"dry-wash.landing.social-proof-section.heading": "We are being chosen",
"dry-wash.arm.master.add": "Add",
"dry-wash.arm.order.title": "Orders",
"dry-wash.arm.order.status.progress": "In Progress",
"dry-wash.arm.order.status.complete": "Completed",
"dry-wash.arm.order.status.pending": "Pending",
"dry-wash.arm.order.status.working": "Working",
"dry-wash.arm.order.status.canceled": "Canceled",
"dry-wash.arm.order.status.placeholder": "Select Status",
"dry-wash.arm.order.table.header.carNumber": "Car Number",
"dry-wash.arm.order.table.header.washingTime": "Washing Time",
"dry-wash.arm.order.table.header.orderDate": "Order Date",
"dry-wash.arm.order.table.header.status": "Status",
"dry-wash.arm.order.table.header.telephone": "Telephone",
"dry-wash.arm.order.table.header.location": "Location",
"dry-wash.arm.master.title": "Masters",
"dry-wash.arm.master.table.header.name": "Name",
"dry-wash.arm.master.table.header.currentJob": "Current Job",
"dry-wash.arm.master.table.header.phone": "Phone",
"dry-wash.arm.master.table.header.actions": "Actions",
"dry-wash.arm.master.table.actionsMenu.delete": "Delete 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.placeholder": "Enter Full Name",
"dry-wash.arm.master.drawer.inputPhone.label": "Phone Number",
"dry-wash.arm.master.drawer.inputPhone.placeholder": "Enter Phone Number",
"dry-wash.arm.master.drawer.button.save": "Save",
"dry-wash.arm.master.drawer.button.cancel": "Cancel",
"dry-wash.arm.master.sideBar.orders": "Orders",
"dry-wash.arm.master.sideBar.master": "Masters",
"dry-wash.arm.master.sideBar.title": "Dry Master",
"dry-wash.notFound.title": "Page Not Found",
"dry-wash.notFound.description": "Unfortunately, the page you are looking for does not exist.",
"dry-wash.notFound.button.back": "Back to Home",
"dry-wash.errorBoundary.title": "Something went wrong",
"dry-wash.errorBoundary.description": "We are already working on fixing the issue",
"dry-wash.errorBoundary.button.reload": "Reload Page"
"dry-wash.landing.social-proof-section.heading": "We are being chosen"
}

@ -3,7 +3,7 @@
"dry-wash.arm.order.title": "Заказы",
"dry-wash.arm.order.status.progress": "Выполняется",
"dry-wash.arm.order.status.complete": "Завершено",
"dry-wash.arm.order.status.pending": "В ожидании",
"dry-wash.arm.order.status.pending": "в ожидании",
"dry-wash.arm.order.status.working": "В работе",
"dry-wash.arm.order.status.canceled": "Отменено",
"dry-wash.arm.order.status.placeholder": "Выберите статус",
@ -26,9 +26,9 @@
"dry-wash.arm.master.drawer.inputPhone.placeholder": "Введите номер телефона",
"dry-wash.arm.master.drawer.button.save": "Сохранить",
"dry-wash.arm.master.drawer.button.cancel": "Отменить",
"dry-wash.arm.master.sideBar.orders": "Заказы",
"dry-wash.arm.master.sideBar.master": "Мастера",
"dry-wash.arm.master.sideBar.title": "Сухой мастер",
"dry-wash.arm.master.sideBar.title": " Сухой мастер",
"dry-wash.arm.master.sideBar.title.master": "Мастера",
"dry-wash.arm.master.sideBar.title.orders": "Заказы",
"dry-wash.landing.benefits-section.description": "Откройте для себя преимущества наших услуг по химчистке автомобилей",
"dry-wash.landing.benefits-section.heading": "Преимущества экологичной автомойки",
"dry-wash.landing.benefits-section.list.0": "Экологически безопасные продукты",
@ -46,8 +46,8 @@
"dry-wash.landing.social-proof-section.heading": "Нас выбирают",
"dry-wash.notFound.title": "Страница не найдена",
"dry-wash.notFound.description": "К сожалению, запрашиваемая вами страница не существует.",
"dry-wash.notFound.button.back": "Вернуться на главную",
"dry-wash.notFound.button.back": " Вернуться на главную",
"dry-wash.errorBoundary.title":"Что-то пошло не так",
"dry-wash.errorBoundary.description": "Мы уже работаем над исправлением проблемы",
"dry-wash.errorBoundary.description": " Мы уже работаем над исправлением проблемы",
"dry-wash.errorBoundary.button.reload": "Перезагрузить страницу"
}

@ -34,16 +34,15 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
render() {
const { hasError } = this.state;
//TODO: добавить анимацию после залива 404 страницы
//TODO: может сделать обертку для хука, чтоб язык менялся без перезагрузки
if (hasError) {
return (
<Center minH='100vh'>
<VStack spacing={4} textAlign='center'>
<Heading as='h1' size='2xl'>
{i18next.t('~:dry-wash.errorBoundary.title')}
{i18next.t('dry-wash.errorBoundary.title')}
</Heading>
<Text fontSize='lg'>
{i18next.t('~:dry-wash.errorBoundary.description')}
{i18next.t('dry-wash.errorBoundary.description')}
</Text>
<Button
colorScheme='teal'
@ -51,7 +50,7 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
variant='outline'
onClick={() => window.location.reload()}
>
{i18next.t('~:dry-wash.errorBoundary.button.reload')}
{i18next.t('dry-wash.errorBoundary.button.reload')}
</Button>
</VStack>
</Center>

@ -7,18 +7,16 @@ import {
IconButton,
} from '@chakra-ui/react';
import { EditIcon } from '@chakra-ui/icons';
import { useTranslation } from 'react-i18next';
import i18next from 'i18next';
const MasterActionsMenu = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.master.table.actionsMenu',
});
return (
<Menu>
<MenuButton icon={<EditIcon />} as={IconButton} variant='outline' />
<MenuList>
<MenuItem>{t('delete')}</MenuItem>
<MenuItem>
{i18next.t('dry-wash.arm.master.table.actionsMenu.delete')}
</MenuItem>
</MenuList>
</Menu>
);

@ -12,7 +12,7 @@ import {
DrawerHeader,
DrawerOverlay,
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import i18next from 'i18next';
const MasterDrawer = ({ isOpen, onClose }) => {
const [newMaster, setNewMaster] = useState({ name: '', phone: '' });
@ -22,44 +22,51 @@ const MasterDrawer = ({ isOpen, onClose }) => {
onClose();
};
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.master.drawer',
});
return (
<Drawer isOpen={isOpen} onClose={onClose} size='md'>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>{t('title')}</DrawerHeader>
<DrawerHeader>
{i18next.t('dry-wash.arm.master.drawer.title')}
</DrawerHeader>
<DrawerBody>
<FormControl mb='4'>
<FormLabel>{t('inputName.label')}</FormLabel>
<FormLabel>
{i18next.t('dry-wash.arm.master.drawer.inputName.label')}
</FormLabel>
<Input
value={newMaster.name}
onChange={(e) =>
setNewMaster({ ...newMaster, name: e.target.value })
}
placeholder={t('inputName.placeholder')}
placeholder={i18next.t(
'dry-wash.arm.master.drawer.inputName.placeholder',
)}
/>
</FormControl>
<FormControl>
<FormLabel> {t('inputPhone.label')}</FormLabel>
<FormLabel>
{' '}
{i18next.t('dry-wash.arm.master.drawer.inputPhone.label')}
</FormLabel>
<Input
value={newMaster.phone}
onChange={(e) =>
setNewMaster({ ...newMaster, phone: e.target.value })
}
placeholder={t('inputPhone.placeholder')}
placeholder={i18next.t(
'dry-wash.arm.master.drawer.inputPhone.placeholder',
)}
/>
</FormControl>
</DrawerBody>
<DrawerFooter>
<Button colorScheme='teal' mr={3} onClick={handleSave}>
{t('button.save')}
{i18next.t('dry-wash.arm.master.drawer.button.save')}
</Button>
<Button variant='ghost' onClick={onClose}>
{t('button.cancel')}
{i18next.t('dry-wash.arm.master.drawer.button.cancel')}
</Button>
</DrawerFooter>
</DrawerContent>

@ -11,39 +11,32 @@ import {
useDisclosure,
Flex,
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { mastersData } from '../../mocks';
import MasterItem from '../MasterItem';
import MasterDrawer from '../MasterDrawer';
const TABLE_HEADERS = [
'name' as const,
'currentJob' as const,
'phone' as const,
'actions' as const,
];
const TABLE_HEADERS = ['name', 'currentJob', 'phone', 'actions'];
const Masters = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.master',
});
return (
<Box p='8'>
<Flex justifyContent='space-between' alignItems='center' mb='5'>
<Heading size='lg'> {t('title')}</Heading>
<Heading size='lg'> {i18next.t('dry-wash.arm.master.title')}</Heading>
<Button colorScheme='green' onClick={onOpen}>
+ {t('add')}
+ {i18next.t('dry-wash.arm.master.add')}
</Button>
</Flex>
<Table variant='simple' colorScheme='blackAlpha'>
<Thead>
<Tr>
{TABLE_HEADERS.map((name) => (
<Th key={name}>{t(`table.header.${name}`)}</Th>
<Th key={name}>
{i18next.t(`dry-wash.arm.master.table.header.${name}`)}
</Th>
))}
</Tr>
</Thead>

@ -1,26 +1,8 @@
import React, { useState } from 'react';
import { Td, Tr, Link, Select } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import i18next from 'i18next';
const statuses = [
'pending' as const,
'progress' as const,
'working' as const,
'canceled' as const,
'complete' as const,
];
type GetArrItemType<ArrType> =
ArrType extends Array<infer ItemType> ? ItemType : never;
export type OrderProps = {
carNumber?: string;
washTime?: string;
orderDate?: string;
status?: GetArrItemType<typeof statuses>;
phone?: string;
location?: string;
};
const statuses = ['pending', 'progress', 'working', 'canceled', 'complete'];
const OrderItem = ({
carNumber,
@ -29,11 +11,7 @@ const OrderItem = ({
status,
phone,
location,
}: OrderProps) => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.order',
});
}) => {
const [statusSelect, setStatus] = useState(status);
return (
@ -44,12 +22,12 @@ const OrderItem = ({
<Td>
<Select
value={statusSelect}
onChange={(e) => setStatus(e.target.value as OrderProps['status'])}
placeholder={t(`status.placeholder`)}
onChange={(e) => setStatus(e.target.value)}
placeholder={i18next.t(`dry-wash.arm.order.status.placeholder`)}
>
{statuses.map((status) => (
<option key={status} value={status}>
{t(`status.${status}`)}
{i18next.t(`dry-wash.arm.order.status.${status}`)}
</option>
))}
</Select>

@ -1,45 +1,36 @@
import React from 'react';
import { Box, Heading, Table, Thead, Tbody, Tr, Th } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import React from 'react';
import i18next from 'i18next';
import { ordersData } from '../../mocks';
import OrderItem from '../OrderItem';
import { OrderProps } from '../OrderItem/OrderItem';
const Orders = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.order',
});
const TABLE_HEADERS = [
'carNumber' as const,
'washingTime' as const,
'orderDate' as const,
'status' as const,
'telephone' as const,
'location' as const,
'carNumber',
'washingTime',
'orderDate',
'status',
'telephone',
'location',
];
return (
<Box p='8'>
<Heading size='lg' mb='5'>
{t('title')}
{i18next.t('dry-wash.arm.order.title')}
</Heading>
<Table variant='simple' colorScheme='blackAlpha'>
<Thead>
<Tr>
{TABLE_HEADERS.map((name, key) => (
<Th key={key}>{t(`table.header.${name}`)}</Th>
<Th key={key}>
{i18next.t(`dry-wash.arm.order.table.header.${name}`)}
</Th>
))}
</Tr>
</Thead>
<Tbody>
{ordersData.map((order, index) => (
<OrderItem
key={index}
{...order}
status={order.status as OrderProps['status']}
/>
<OrderItem key={index} {...order} />
))}
</Tbody>
</Table>

@ -1,14 +1,10 @@
import { Box, Button, Heading, VStack, Divider } from '@chakra-ui/react';
import { Box, Button, Heading, VStack } from '@chakra-ui/react';
import React from 'react';
import { Divider } from '@chakra-ui/react';
import i18next from 'i18next';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
const Sidebar = () => {
const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.arm.master.sideBar',
});
return (
const Sidebar = () => (
<Box
borderRight='1px solid black'
bg='gray.50'
@ -18,7 +14,7 @@ const Sidebar = () => {
pt='8'
>
<Heading color='green' size='lg' mb='5'>
{t('title')}
{i18next.t(`dry-wash.arm.master.sideBar.title`)}
</Heading>
<VStack align='start' spacing='4'>
@ -30,7 +26,7 @@ const Sidebar = () => {
colorScheme='green'
variant='ghost'
>
{t('orders')}
{i18next.t(`dry-wash.arm.master.sideBar.title.orders`)}
</Button>
<Divider />
<Button
@ -40,12 +36,11 @@ const Sidebar = () => {
colorScheme='green'
variant='ghost'
>
{t('master')}
{i18next.t(`dry-wash.arm.master.sideBar.title.master`)}
</Button>
<Divider />
</VStack>
</Box>
);
};
);
export default Sidebar;

@ -15,7 +15,7 @@ export const CtaButton: FC<ButtonProps> = (props) => {
colorScheme='primary'
{...props}
>
{t('~:dry-wash.landing.make-order-button')}
{t('dry-wash.landing.make-order-button')}
</Button>
);
};

@ -7,7 +7,7 @@ import { LogoSvg } from '../../../assets/icons';
export const SiteLogo: FC = () => {
const { t } = useTranslation();
return <Image src={LogoSvg} alt={t('~:dry-wash.landing.site-logo')} w={40} />;
return <Image src={LogoSvg} alt={t('dry-wash.landing.site-logo')} w={40} />;
};
// todo: replace Image by SVG React component

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import LayoutArm from '../../components/LayoutArm';