diff --git a/.env b/.env deleted file mode 100644 index 970babe..0000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -BASE_URL=http://localhost:4455 -NEXTAUTH_SECRET=SOME_SECRET -NEXTAUTH_URL=http://localhost:3000 diff --git a/public/images/avatar.jpg b/public/images/avatar.jpg deleted file mode 100644 index 19ba5b8..0000000 Binary files a/public/images/avatar.jpg and /dev/null differ diff --git a/public/svgs/organisation-default.svg b/public/svgs/organisation-default.svg new file mode 100644 index 0000000..60939e3 --- /dev/null +++ b/public/svgs/organisation-default.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4c9aaf1..0773bc1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,7 +13,11 @@ export const metadata: Metadata = { description: 'Generated by create next app', }; -export default function RootLayout({ children }: { children: ReactNode }) { +export default async function RootLayout({ + children, +}: { + children: ReactNode; +}) { return ( diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index f95d955..c0feb5a 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -6,12 +6,6 @@ import Image from 'next/image'; import { useRouter } from 'next/navigation'; import Button from '@/components/common/ui/button'; -import { - ButtonColor, - ButtonIcon, - ButtonSize, - ButtonVariant, -} from '@/components/common/ui/button/types'; import * as styles from '../components/pages/NotFound.styles'; @@ -30,13 +24,7 @@ const NotFound: FC = () => {
Ви можете написати в підтримку, якщо ви впевнені, що виникла помилка - diff --git a/src/app/organisations/Organisations.styles.ts b/src/app/organisations/Organisations.styles.ts new file mode 100644 index 0000000..ccc1348 --- /dev/null +++ b/src/app/organisations/Organisations.styles.ts @@ -0,0 +1,7 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const wrapper: SxProps = { + width: '100%', + display: 'grid', + placeItems: 'center', +}; diff --git a/src/app/organisations/[id]/Organisation.styles.ts b/src/app/organisations/[id]/Organisation.styles.ts new file mode 100644 index 0000000..5b1743e --- /dev/null +++ b/src/app/organisations/[id]/Organisation.styles.ts @@ -0,0 +1,55 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const layout: SxProps = { + p: { + mobile: '16px', + tablet: '32px', + desktop: '48px 100px 36px 100px', + }, + display: 'flex', + justifyContent: 'center', + backgroundColor: 'gray.700', + flexDirection: { + mobile: 'column', + desktop: 'row', + }, + gap: { + mobile: '10px', + desktop: '20px', + }, +}; + +export const wrapper: SxProps = { + width: '100%', +}; + +export const search: SxProps = { + width: '100%', + my: '16px', + display: 'inline-flex', + gap: '16px', +}; + +export const input: SxProps = { + width: '100%', + backgroundColor: 'white', + '.MuiInputBase-root': { + backgroundColor: 'white!important', + }, +}; + +export const items: SxProps = { + pr: '5px', + pb: '5px', + maxHeight: { + mobile: '100%', + desktop: '57vh', + }, + overflowY: 'auto', + + '::-webkit-scrollbar': { + width: '0px', + }, + scrollbarWidth: 'none', + msOverflowStyle: 'none', +}; diff --git a/src/app/organisations/[id]/components/info-card/InfoCard.styles.ts b/src/app/organisations/[id]/components/info-card/InfoCard.styles.ts new file mode 100644 index 0000000..62fd32e --- /dev/null +++ b/src/app/organisations/[id]/components/info-card/InfoCard.styles.ts @@ -0,0 +1,8 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const wrapper: SxProps = { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-start', + gap: '20px', +}; diff --git a/src/app/organisations/[id]/components/info-card/InfoCard.tsx b/src/app/organisations/[id]/components/info-card/InfoCard.tsx new file mode 100644 index 0000000..298a6f7 --- /dev/null +++ b/src/app/organisations/[id]/components/info-card/InfoCard.tsx @@ -0,0 +1,22 @@ +import { FC } from 'react'; +import { Box, Typography } from '@mui/material'; + +import * as styles from './InfoCard.styles'; + +interface InfoCardProps { + name: string; + value: string; +} + +const InfoCard: FC = ({ name, value }) => { + return ( + + + {name} + + {value} + + ); +}; + +export default InfoCard; diff --git a/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.styles.ts b/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.styles.ts new file mode 100644 index 0000000..038d52d --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.styles.ts @@ -0,0 +1,31 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const wrapper: SxProps = { + maxWidth: { + mobile: '100%', + tablet: '603px', + }, + p: '8px', + + display: 'inline-flex', + gap: '20px', + + float: 'left', + overflow: 'scroll', + scrollbarWidth: 'none', + '&::-webkit-scrollbar': { + width: '0', + height: '0', + }, + + backgroundColor: 'white.main', + boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.25)', + borderRadius: '6px', +}; + +export const button = (isActive: boolean): SxProps => ({ + ...(isActive && { + backgroundColor: 'rgba(255, 84, 30, 0.35)', + color: 'orange.100', + }), +}); diff --git a/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.tsx b/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.tsx new file mode 100644 index 0000000..a3ef197 --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-bar/OrganisationBar.tsx @@ -0,0 +1,36 @@ +'use client'; +import { FC } from 'react'; +import { Box } from '@mui/material'; +import Link from 'next/link'; +import { usePathname, useSearchParams } from 'next/navigation'; + +import { tabs } from '@/app/organisations/[id]/components/organisation-bar/constants'; +import Button from '@/components/common/ui/button'; + +import * as styles from './OrganisationBar.styles'; + +interface OrganisationBarProps {} + +const OrganisationBar: FC = () => { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const curTab = searchParams.get('tab'); + + return ( + + {tabs.map((tab) => ( + + + + ))} + + ); +}; + +export default OrganisationBar; diff --git a/src/app/organisations/[id]/components/organisation-bar/constants/index.ts b/src/app/organisations/[id]/components/organisation-bar/constants/index.ts new file mode 100644 index 0000000..9008e46 --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-bar/constants/index.ts @@ -0,0 +1,18 @@ +export const tabs = [ + { + name: 'Працівники', + href: 'employees', + }, + { + name: 'Послуги', + href: 'services', + }, + { + name: 'Клієнти', + href: 'clients', + }, + { + name: 'Автомобілі', + href: 'cars', + }, +]; diff --git a/src/app/organisations/[id]/components/organisation-bar/index.ts b/src/app/organisations/[id]/components/organisation-bar/index.ts new file mode 100644 index 0000000..a4f54f9 --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-bar/index.ts @@ -0,0 +1 @@ +export { default } from './OrganisationBar'; diff --git a/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.styles.ts b/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.styles.ts new file mode 100644 index 0000000..2fc0558 --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.styles.ts @@ -0,0 +1,24 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const wrapper: SxProps = { + flex: '0 0 auto', + maxWidth: { + mobile: '100%', + desktop: '400px', + }, + height: 'fit-content', + p: '16px', + display: 'flex', + flexDirection: 'column', + gap: '16px', + backgroundColor: 'white.main', + + borderRadius: '4px', + boxShadow: '6px 6px 4px 0px rgba(0, 0, 0, 0.25)', + + img: { + borderRadius: '4px', + width: '100%', + height: 'auto', + }, +}; diff --git a/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.tsx b/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.tsx new file mode 100644 index 0000000..78e5b7c --- /dev/null +++ b/src/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard.tsx @@ -0,0 +1,45 @@ +import { FC } from 'react'; +import { Box, Typography } from '@mui/material'; +import Image from 'next/image'; + +import InfoCard from '@/app/organisations/[id]/components/info-card/InfoCard'; +import Button from '@/components/common/ui/button'; + +import * as styles from './OrganisationDashboard.styles'; + +interface OrganisationDashboardProps { + organisation: { + avatar: string; + name: string; + address: string; + owner: string; + description?: string; + }; +} + +const OrganisationDashboard: FC = ({ + organisation, +}) => { + return ( + + organisation picture + {organisation.name} + + + + + {organisation.description} + + + + + + ); +}; + +export default OrganisationDashboard; diff --git a/src/app/organisations/[id]/layout.tsx b/src/app/organisations/[id]/layout.tsx new file mode 100644 index 0000000..bb795b0 --- /dev/null +++ b/src/app/organisations/[id]/layout.tsx @@ -0,0 +1,26 @@ +import { FC, PropsWithChildren } from 'react'; +import { Box } from '@mui/material'; + +import OrganisationDashboard from '@/app/organisations/[id]/components/organisation-dasboard/OrganisationDashboard'; + +import * as styles from './Organisation.styles'; + +const organisation = { + avatar: '/svgs/organisation-default.svg', + name: 'Автосервіс Гепард', + address: 'м. Київ, проспект Перемоги 10', + owner: 'Новиков Ігор Михайлович', + description: + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur culpa cupiditate dignissimos dolorem dolores, error expedita fugit id itaque minima minus molestias nemo, nobis nostrum nulla placeat quis repudiandae! Quod.\n', +}; + +const OrganisationLayout: FC = ({ children }) => { + return ( + + + {children} + + ); +}; + +export default OrganisationLayout; diff --git a/src/app/organisations/[id]/page.tsx b/src/app/organisations/[id]/page.tsx new file mode 100644 index 0000000..c1a62de --- /dev/null +++ b/src/app/organisations/[id]/page.tsx @@ -0,0 +1,31 @@ +import { Box, Stack } from '@mui/material'; + +import OrganisationBar from '@/app/organisations/[id]/components/organisation-bar'; +import { mocks } from '@/app/profile/components/tabs/constants'; +import Card from '@/components/common/ui/card'; +import Position from '@/components/common/ui/card/components/Position'; +import IconButton from '@/components/common/ui/icon-button/IconButton'; +import Input from '@/components/common/ui/input/Input'; + +import * as styles from './Organisation.styles'; + +const OrganisationPage = () => { + return ( + + + + + + + + {mocks.map((mock) => ( + + + + ))} + + + ); +}; + +export default OrganisationPage; diff --git a/src/app/organisations/components/organisations-llist/OrganisationsList.styles.ts b/src/app/organisations/components/organisations-llist/OrganisationsList.styles.ts new file mode 100644 index 0000000..5e5f868 --- /dev/null +++ b/src/app/organisations/components/organisations-llist/OrganisationsList.styles.ts @@ -0,0 +1,30 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const wrapper: SxProps = { + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}; + +export const search: SxProps = { + maxWidth: '794px', + width: '100%', + mt: '26px', + display: 'inline-flex', + gap: '16px', +}; + +export const input: SxProps = { + maxWidth: '730px', + flexGrow: 1, + mb: '26px', +}; + +export const organisations: SxProps = { + width: '100%', + maxWidth: '994px', + display: 'grid', + gap: '16px', + mb: '64px', +}; diff --git a/src/app/organisations/components/organisations-llist/OrganisationsList.tsx b/src/app/organisations/components/organisations-llist/OrganisationsList.tsx new file mode 100644 index 0000000..ce102cb --- /dev/null +++ b/src/app/organisations/components/organisations-llist/OrganisationsList.tsx @@ -0,0 +1,43 @@ +'use client'; +import { FC, useState } from 'react'; +import { Box } from '@mui/material'; + +import IconButton from '@/components/common/ui/icon-button/IconButton'; +import Input from '@/components/common/ui/input/Input'; +import OrganisationPopup from '@/components/common/ui/pop-ups/organisation-popup/OrganisationPopup'; + +import * as styles from './OrganisationsList.styles'; + +interface OrganisationsListProps {} + +const OrganisationsList: FC = () => { + const [open, setOpen] = useState(false); + + const handleClick = () => { + setOpen(!open); + }; + + return ( + + + + + + + + + {/*{mocks.map((organisation) => (*/} + {/* */} + {/* */} + {/* */} + {/*))}*/} + + + ); +}; + +export default OrganisationsList; diff --git a/src/app/organisations/components/organisations-llist/index.ts b/src/app/organisations/components/organisations-llist/index.ts new file mode 100644 index 0000000..119f03d --- /dev/null +++ b/src/app/organisations/components/organisations-llist/index.ts @@ -0,0 +1 @@ +export { default } from './OrganisationsList'; diff --git a/src/app/organisations/page.tsx b/src/app/organisations/page.tsx new file mode 100644 index 0000000..06b1d28 --- /dev/null +++ b/src/app/organisations/page.tsx @@ -0,0 +1,18 @@ +import { Box, Typography } from '@mui/material'; + +import OrganisationsList from '@/app/organisations/components/organisations-llist'; + +import * as styles from './Organisations.styles'; + +const OrganisationsPage = () => { + return ( + + + Мої організації + + + + ); +}; + +export default OrganisationsPage; diff --git a/src/app/profile/components/profile-card/ProfileCard.tsx b/src/app/profile/components/profile-card/ProfileCard.tsx index 6865ad5..b78f5a7 100644 --- a/src/app/profile/components/profile-card/ProfileCard.tsx +++ b/src/app/profile/components/profile-card/ProfileCard.tsx @@ -27,7 +27,7 @@ export const ProfileCardContext = createContext( ); const ProfileCard: FC = () => { - const { user, isLoading } = useAuthContext(); + const { user, isLoading, isError } = useAuthContext(); const birthday = getDate(user?.birthDate as string); const years = getYears(user?.birthDate as string); const [open, setOpen] = useState(false); @@ -38,7 +38,7 @@ const ProfileCard: FC = () => { return ( - {isLoading ? ( + {isError || (!user && isLoading) ? ( ) : ( diff --git a/src/app/profile/components/profile-card/popup/ProfileEditPopup.tsx b/src/app/profile/components/profile-card/popup/ProfileEditPopup.tsx index 52c5930..f753420 100644 --- a/src/app/profile/components/profile-card/popup/ProfileEditPopup.tsx +++ b/src/app/profile/components/profile-card/popup/ProfileEditPopup.tsx @@ -32,7 +32,7 @@ const ProfileEditPopup: FC = () => { const [activeSection, setActiveSection] = useState('personalData'); const { user } = useAuthContext(); - const [avatar, setAvatar] = useState(user!.avatar); + const [avatar, setAvatar] = useState(user?.avatar || ''); const fileInputRef = useRef(null); const toast = useToast(); diff --git a/src/app/profile/components/profile-header/ProfileHeader.tsx b/src/app/profile/components/profile-header/ProfileHeader.tsx index 8c3c7a0..0792837 100644 --- a/src/app/profile/components/profile-header/ProfileHeader.tsx +++ b/src/app/profile/components/profile-header/ProfileHeader.tsx @@ -1,37 +1,27 @@ -'use client'; - import React, { FC } from 'react'; -import { Box, Skeleton, Typography } from '@mui/material'; -import { useRouter } from 'next/navigation'; +import { Box, Typography } from '@mui/material'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; import LogoutButton from '@/components/common/ui/logout-button/LogoutButton'; -import { useAuthContext } from '@/hooks/use-auth/auth-context/AuthContext'; -import StorageUtil from '@/lib/utils/storageUtil'; import * as styles from './ProfileHeader.styles'; const ProfileHeader: FC = () => { - const router = useRouter(); - const { isLoading, mutate } = useAuthContext(); const logout = async () => { - StorageUtil.deleteToken(); - await mutate(); - router.push('/login'); + 'use server'; + + cookies().delete('accessToken'); + redirect('/login'); }; return ( Профіль - {isLoading ? ( - - ) : ( - logout()}>Вихід - )} + +
+ Вихід +
); }; diff --git a/src/app/profile/components/tabs/constants/index.tsx b/src/app/profile/components/tabs/constants/index.tsx index 90851fa..28ca6ed 100644 --- a/src/app/profile/components/tabs/constants/index.tsx +++ b/src/app/profile/components/tabs/constants/index.tsx @@ -24,19 +24,34 @@ export const tabs = [ ]; export const mocks = [ - { - avatar: '/images/avatar.jpg', - name: 'Автосервіс Гепард', - position: 'Власник', - }, - { - avatar: '/images/avatar.jpg', - name: 'Автосервіс Чірік', - position: 'Співвласник', - }, - { - avatar: '/images/avatar.jpg', - name: 'Автосервіс Лісовик', - position: 'Співробітник', - }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Гепард', + // position: 'Власник', + // }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Чірік', + // position: 'Співвласник', + // }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Лісовик', + // position: 'Співробітник', + // }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Гепард', + // position: 'Власник', + // }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Чірік', + // position: 'Співвласник', + // }, + // { + // avatar: '/images/avatar.jpg', + // name: 'Автосервіс Лісовик', + // position: 'Співробітник', + // }, ]; diff --git a/src/app/profile/components/tabs/contents/Organisations.tsx b/src/app/profile/components/tabs/contents/Organisations.tsx index cab8fd2..d881976 100644 --- a/src/app/profile/components/tabs/contents/Organisations.tsx +++ b/src/app/profile/components/tabs/contents/Organisations.tsx @@ -1,55 +1,77 @@ -import { FC } from 'react'; +import { FC, useState } from 'react'; +import { PencilSquareIcon } from '@heroicons/react/24/outline'; import { Box, Stack, Typography } from '@mui/material'; import { mocks } from '@/app/profile/components/tabs/constants'; import Gear from '@/components/common/icons/Gear'; import Button from '@/components/common/ui/button'; +import Card from '@/components/common/ui/card'; +import Position from '@/components/common/ui/card/components/Position'; +import OrganisationPopup from '@/components/common/ui/pop-ups/organisation-popup/OrganisationPopup'; -import OrganisationCard from '../../../../../components/common/ui/organisation-card'; import * as styles from '../Tabs.styles'; const Organisations: FC = () => { + const [open, setOpen] = useState(false); + + const handleClick = () => { + setOpen(!open); + console.log(open); + }; + if (mocks.length === 0) { return ( - - - - - У вас покищо немає доданих організацій! -
- Ви можете приєднатися до організації або створити власну. -
- - - + + + + + + У вас поки немає доданих організацій! +
+ Ви можете створити власну або власники інших організацій можуть + додати вас до свого складу :) +
+ + +
-
-
+
+ ); } return ( - - {mocks.map((organisation) => ( - - ))} - - + + + {mocks.map((organisation) => ( + + + + ))} + + + ); }; diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 2de64c9..06bdb10 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -7,7 +7,7 @@ import ProfileHeader from '@/app/profile/components/profile-header'; import Tabs from './components/tabs'; import * as styles from './ProfilePage.styles'; -const Profile = () => { +const Profile = async () => { return ( diff --git a/src/app/test/button/page.tsx b/src/app/test/button/page.tsx index f070c09..cc5142a 100644 --- a/src/app/test/button/page.tsx +++ b/src/app/test/button/page.tsx @@ -114,6 +114,12 @@ const ButtonPage = () => { + {' '} + diff --git a/src/app/test/icon-button/page.tsx b/src/app/test/icon-button/page.tsx new file mode 100644 index 0000000..198f014 --- /dev/null +++ b/src/app/test/icon-button/page.tsx @@ -0,0 +1,18 @@ +import { Box } from '@mui/material'; + +import IconButton from '@/components/common/ui/icon-button/IconButton'; + +const Page = () => { + return ( + + + + + + + + + ); +}; + +export default Page; diff --git a/src/app/test/organisation-card/page.tsx b/src/app/test/organisation-card/page.tsx index 7ac26fb..791e88d 100644 --- a/src/app/test/organisation-card/page.tsx +++ b/src/app/test/organisation-card/page.tsx @@ -1,6 +1,9 @@ import { Stack, Typography } from '@mui/material'; -import OrganisationCard from '@/components/common/ui/organisation-card'; +import { Card, Position, Price } from '@/components/common/ui/card/Card'; +import Auto from '@/components/common/ui/card/components/Auto'; +import Phone from '@/components/common/ui/card/components/Phone'; +import type { Auto as IAuto } from '@/types/auto'; const mocks = [ { @@ -24,18 +27,64 @@ const mocks = [ }, ]; +const autoMocks: IAuto[] = [ + { + id: 1, + number: 'ABC123', + brand: 'Toyota', + model: 'Corolla', + year: 2020, + vin: '1HGBH41JXMN109186', + engine: '1.8L', + volume: 1800, + }, + { + id: 2, + number: 'XYZ789', + brand: 'Honda', + model: 'Civic', + year: 2018, + vin: '2HGBH41JXMN109187', + engine: '2.0L', + volume: 2000, + }, + { + id: 3, + number: 'DEF456', + brand: 'Ford', + model: 'Focus', + year: 2019, + vin: '3HGBH41JXMN109188', + engine: '1.5L', + volume: 1500, + }, +]; + const Page = () => { return ( Organisation Card {mocks.map((mock, index) => ( - + > + + + ))} + + + + + + + + {autoMocks.map((auto: IAuto) => ( + + + ))} ); diff --git a/src/components/common/layout/page-layout/footer/Footer.styles.ts b/src/components/common/layout/page-layout/footer/Footer.styles.ts index 5a9537b..3c4e62f 100644 --- a/src/components/common/layout/page-layout/footer/Footer.styles.ts +++ b/src/components/common/layout/page-layout/footer/Footer.styles.ts @@ -10,14 +10,11 @@ export const wrapper: SxProps = { desktop: 'flex-start', mobile: 'center', }, - justifyContent: { - desktop: 'space-between', - mobile: 'center', - }, - zIndex: 11, + zIndex: 10, padding: { - mobileMedium: '38px 100px', + desktop: '38px 100px', + mobileMedium: '38px 50px', mobile: '16px', }, height: 'auto', @@ -41,7 +38,13 @@ export const text: SxProps = { }; export const columns: SxProps = { + width: '100%', display: 'flex', + justifyContent: { + desktop: 'flex-end', + mobileMedium: 'center', + mobile: 'space-between', + }, gap: { desktop: '80px', mobileMedium: '40px', diff --git a/src/components/common/layout/page-layout/footer/constants/index.ts b/src/components/common/layout/page-layout/footer/constants/index.ts index 0d91d13..97444e6 100644 --- a/src/components/common/layout/page-layout/footer/constants/index.ts +++ b/src/components/common/layout/page-layout/footer/constants/index.ts @@ -8,8 +8,8 @@ export const links: Links = { href: '/', }, { - title: 'Організація', - href: '/organization', + title: 'Організації', + href: '/organizations', }, { title: 'Ціни', diff --git a/src/components/common/layout/page-layout/header/Header.styles.ts b/src/components/common/layout/page-layout/header/Header.styles.ts index 50475a2..b175d7a 100644 --- a/src/components/common/layout/page-layout/header/Header.styles.ts +++ b/src/components/common/layout/page-layout/header/Header.styles.ts @@ -1,7 +1,12 @@ import { SxProps, Theme } from '@mui/material/styles'; export const wrapper: SxProps = { - p: '0px 100px', + py: 0, + px: { + mobile: '16px', + tablet: '32px', + desktop: '100px', + }, width: '100%', height: '64px', boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px', diff --git a/src/components/common/layout/page-layout/header/menu/constants/menuItems.ts b/src/components/common/layout/page-layout/header/menu/constants/menuItems.ts index 8a613f1..959786f 100644 --- a/src/components/common/layout/page-layout/header/menu/constants/menuItems.ts +++ b/src/components/common/layout/page-layout/header/menu/constants/menuItems.ts @@ -4,8 +4,8 @@ export const menuItems = [ url: '/', }, { - title: 'Організація', - url: '/organisation', + title: 'Організації', + url: '/organisations', }, { title: 'Ціни', diff --git a/src/components/common/ui/button/Button.styles.ts b/src/components/common/ui/button/Button.styles.ts index a9160fa..fd20dd6 100644 --- a/src/components/common/ui/button/Button.styles.ts +++ b/src/components/common/ui/button/Button.styles.ts @@ -73,6 +73,27 @@ const outlinedWhiteColors: SxProps = { }, }; +const textColors: SxProps = { + backgroundColor: 'transparent', + borderColor: 'transparent', + color: 'black.main', + '&:hover': { + backgroundColor: 'rgba(255, 84, 30, 0.15);', + borderColor: 'transparent', + }, + '&:active': { + backgroundColor: 'rgba(255, 84, 30, 0.35)', + color: 'orange.100', + }, + '&:focus': { + backgroundColor: 'rgba(255, 84, 30, 0.15)', + borderColor: 'orange.300', + }, + '&.Mui-disabled': { + color: 'gray.500', + }, +}; + export const button = ( variant: ButtonVariant, size: ButtonSize, @@ -85,23 +106,25 @@ export const button = ( borderRadius: '6px', textTransform: 'none', display: 'flex', + + ...(size === 'small' && { + typography: 'body1Bold', + p: '10px 20px', + gap: '8px', + }), + ...(size === 'medium' && { + p: '12px 28px', + typography: 'h6Bold', + gap: '12px', + }), + ...(size === 'large' && { + p: '16px 32px', + gap: '12px', + typography: 'h6Bold', + }), + ...(variant === 'contained' && { ...containedColors, - ...(size === 'small' && { - typography: 'body1Bold', - p: '10px 20px', - gap: '8px', - }), - ...(size === 'medium' && { - p: '12px 28px', - typography: 'h6Bold', - gap: '12px', - }), - ...(size === 'large' && { - p: '16px 32px', - gap: '12px', - typography: 'h6Bold', - }), }), ...(variant === 'outlined' && { ...(color === 'primary' && { @@ -110,20 +133,11 @@ export const button = ( ...(color === 'secondary' && { ...outlinedWhiteColors, }), - ...(size === 'small' && { - typography: 'body1Bold', - p: '10px 20px', - gap: '8px', - }), - ...(size === 'medium' && { - p: '12px 28px', - typography: 'h6Bold', - gap: '12px', - }), - ...(size === 'large' && { - p: '16px 32px', - gap: '12px', - typography: 'h6Bold', - }), + }), + ...(variant === 'text' && { + p: '10px 24px', + border: '1px solid', + typography: 'body1Medium', + ...textColors, }), }); diff --git a/src/components/common/ui/button/Button.tsx b/src/components/common/ui/button/Button.tsx index 7b90ff1..d6ed4b9 100644 --- a/src/components/common/ui/button/Button.tsx +++ b/src/components/common/ui/button/Button.tsx @@ -27,7 +27,11 @@ const Button: FC = ({ ...rest }) => { return ( - + {icon === 'left' && iconComponent} {children} {icon === 'right' && iconComponent} diff --git a/src/components/common/ui/button/types/index.ts b/src/components/common/ui/button/types/index.ts index d9f08a1..c7cc625 100644 --- a/src/components/common/ui/button/types/index.ts +++ b/src/components/common/ui/button/types/index.ts @@ -2,6 +2,6 @@ export type ButtonSize = 'small' | 'medium' | 'large'; export type ButtonIcon = 'none' | 'left' | 'right'; -export type ButtonVariant = 'contained' | 'outlined'; +export type ButtonVariant = 'contained' | 'outlined' | 'text'; export type ButtonColor = 'primary' | 'secondary'; diff --git a/src/components/common/ui/card/Card.styles.ts b/src/components/common/ui/card/Card.styles.ts new file mode 100644 index 0000000..fe816b9 --- /dev/null +++ b/src/components/common/ui/card/Card.styles.ts @@ -0,0 +1,107 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +const defaultTextSettings = { + typography: 'body1', + color: 'black', +}; + +export const wrapper: SxProps = { + width: '100%', + + display: 'block', + borderRadius: '6px', + border: '1px solid', + borderColor: 'gray.500', + backgroundColor: 'gray.700', + boxShadow: '2px 2px 4px 0px rgba(0, 0, 0, 0.25)', + + '&:hover': { + backgroundColor: 'gray.600', + }, + + '&:focus': { + outline: '2px solid', + outlineColor: 'gray.100', + }, +}; + +export const box: SxProps = { + flex: '1 1 auto', + height: 'auto', + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', +}; + +export const content: SxProps = { + p: { + mobile: '12px 0 12px 20px', + }, +}; + +export const title: SxProps = { + display: 'flex', + justifyContent: 'space-between', +}; + +export const info: SxProps = { + display: 'grid', + gridTemplateColumns: 'auto 1fr', + columnGap: '20px', +}; + +export const heading: SxProps = { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'flex-start', + gap: '8px', +}; + +export const avatar: SxProps = { + width: '64px', + height: '64px', + border: '1px solid', + borderColor: 'gray.500', +}; + +export const price: SxProps = { + typography: 'body1Bold', + color: 'orange.100', +}; + +export const phone: SxProps = defaultTextSettings; +export const auto: SxProps = defaultTextSettings; + +export const description: SxProps = { + mt: '14px', + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 3, + WebkitBoxOrient: 'vertical', +}; + +export const button: SxProps = { + alignSelf: 'stretch', + width: { + mobile: '28px', + tablet: '44px', + }, + height: { + mobile: 'auto', + tablet: '44px', + }, + borderRadius: { + tablet: '50%', + mobile: '0 6px 6px 0', + }, + m: { + mobile: 0, + tablet: '22px 16px 0 0', + }, + p: { + mobile: '4px', + tablet: 0, + }, +}; diff --git a/src/components/common/ui/card/Card.tsx b/src/components/common/ui/card/Card.tsx new file mode 100644 index 0000000..b83fa06 --- /dev/null +++ b/src/components/common/ui/card/Card.tsx @@ -0,0 +1,56 @@ +'use client'; +import { FC } from 'react'; +import { Avatar, Box, Typography } from '@mui/material'; +import Link from 'next/link'; + +import Position from '@/components/common/ui/card/components/Position'; +import Price from '@/components/common/ui/card/components/Price'; +import RoundButtonIcon from '@/components/common/ui/round-button-icon'; +import useToast from '@/hooks/use-toast'; +import mergeSx from '@/lib/utils/mergeSx'; + +import * as styles from './Card.styles'; +import type { CardProps } from './types'; + +const CardComponent: FC = ({ + avatar, + name, + description = '', + href = '', + children, + sx = {}, +}) => { + const toast = useToast(); + const handleDelete = (event: React.MouseEvent) => { + event.stopPropagation(); + toast.success('Deleted', '', 3000); + }; + + return ( + + + + + + + {avatar && } + + {name} + {children} + + + + {description && ( + {description} + )} + + + + + + ); +}; + +export const Card = Object.assign(CardComponent, { Position, Price }); + +export { CardComponent, Position, Price }; diff --git a/src/components/common/ui/card/components/Auto.tsx b/src/components/common/ui/card/components/Auto.tsx new file mode 100644 index 0000000..2115a57 --- /dev/null +++ b/src/components/common/ui/card/components/Auto.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react'; +import { Typography } from '@mui/material'; + +import * as styles from '@/components/common/ui/card/Card.styles'; +import { AutoProps } from '@/components/common/ui/card/types'; + +const Auto: FC = ({ auto }) => { + return ( + <> + Бренд: {auto.brand} + Модель: {auto.model} + Рік: {auto.year} + VIN код: {auto.vin} + Двигун: {auto.engine} + Об'єм двигуна: {auto.volume}L + + ); +}; + +export default Auto; diff --git a/src/components/common/ui/card/components/Phone.tsx b/src/components/common/ui/card/components/Phone.tsx new file mode 100644 index 0000000..4badc59 --- /dev/null +++ b/src/components/common/ui/card/components/Phone.tsx @@ -0,0 +1,11 @@ +import { FC } from 'react'; +import { Typography } from '@mui/material'; + +import * as styles from '@/components/common/ui/card/Card.styles'; +import type { NumberProps } from '@/components/common/ui/card/types'; + +const Phone: FC = ({ phone }) => ( + телефон: {phone} +); + +export default Phone; diff --git a/src/components/common/ui/card/components/Position.tsx b/src/components/common/ui/card/components/Position.tsx new file mode 100644 index 0000000..547c87f --- /dev/null +++ b/src/components/common/ui/card/components/Position.tsx @@ -0,0 +1,11 @@ +import { FC } from 'react'; + +import Tag from '@/components/common/ui/tag'; + +import type { PositionProps } from '../types'; + +const Position: FC = ({ position }) => ( + +); + +export default Position; diff --git a/src/components/common/ui/card/components/Price.tsx b/src/components/common/ui/card/components/Price.tsx new file mode 100644 index 0000000..73eb374 --- /dev/null +++ b/src/components/common/ui/card/components/Price.tsx @@ -0,0 +1,11 @@ +import { FC } from 'react'; +import { Typography } from '@mui/material'; + +import * as styles from '@/components/common/ui/card/Card.styles'; +import type { PriceProps } from '@/components/common/ui/card/types'; + +const Price: FC = ({ price }) => ( + {price} +); + +export default Price; diff --git a/src/components/common/ui/card/index.ts b/src/components/common/ui/card/index.ts new file mode 100644 index 0000000..d9c55c8 --- /dev/null +++ b/src/components/common/ui/card/index.ts @@ -0,0 +1 @@ +export { Card as default } from './Card'; diff --git a/src/components/common/ui/card/types/index.ts b/src/components/common/ui/card/types/index.ts new file mode 100644 index 0000000..3b08ad0 --- /dev/null +++ b/src/components/common/ui/card/types/index.ts @@ -0,0 +1,29 @@ +import { ReactNode } from 'react'; +import { SxProps, Theme } from '@mui/material/styles'; + +import { Auto } from '@/types/auto'; + +export interface CardProps { + avatar?: string; + name: string; + description?: string; + href?: string; + children?: ReactNode; + sx?: SxProps; +} + +export interface PositionProps { + position: string; +} + +export interface PriceProps { + price: string; +} + +export interface NumberProps { + phone: string; +} + +export interface AutoProps { + auto: Auto; +} diff --git a/src/components/common/ui/icon-button/IconButton.styles.ts b/src/components/common/ui/icon-button/IconButton.styles.ts new file mode 100644 index 0000000..00cbf3f --- /dev/null +++ b/src/components/common/ui/icon-button/IconButton.styles.ts @@ -0,0 +1,93 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +import { + ButtonColors, + IconButtonSize, + IconButtonVariant, +} from '@/components/common/ui/icon-button/types'; + +const buttonColour: ButtonColors = { + contained: { + default: { + color: 'orange.300', + border: 'orange.300', + }, + hover: { + color: 'orange.200', + border: 'orange.200', + }, + active: { + color: 'orange.400', + border: 'orange.400', + }, + focused: { + color: 'orange.300', + border: 'orange.100', + }, + disabled: { + color: 'gray.200', + border: 'gray.200', + }, + }, + outlined: { + default: { + color: 'transparent', + border: 'orange.300', + }, + hover: { + color: 'rgba(255, 84, 30, 0.15)', + border: 'orange.300', + }, + active: { + color: 'rgba(255, 84, 30, 0.50)', + border: 'orange.300', + }, + focused: { + color: 'rgba(255, 84, 30, 0.30)', + border: 'orange.300', + }, + disabled: { + color: 'transparent', + border: 'gray.200', + }, + }, +}; + +export const button = ( + size: IconButtonSize, + variant: IconButtonVariant, +): SxProps => ({ + borderRadius: '6px', + border: '2px solid', + p: 0, + width: size === 'small' ? '36px' : '48px', + height: size === 'small' ? '36px' : '48px', + + backgroundColor: buttonColour[variant].default.color, + borderColor: buttonColour[variant].default.border, + '&:hover': { + backgroundColor: buttonColour[variant].hover.color, + borderColor: buttonColour[variant].hover.border, + }, + '&:active': { + backgroundColor: buttonColour[variant].active.color, + borderColor: buttonColour[variant].active.border, + }, + '&:focus': { + backgroundColor: buttonColour[variant].focused.color, + borderColor: buttonColour[variant].focused.border, + }, + '&:disabled': { + backgroundColor: buttonColour[variant].disabled.color, + borderColor: buttonColour[variant].disabled.border, + svg: { + stroke: variant === 'contained' ? 'white' : '#949394', + }, + }, + + svg: { + width: '24px', + height: '24px', + stroke: variant === 'contained' ? 'white' : 'black', + }, +}); diff --git a/src/components/common/ui/icon-button/IconButton.tsx b/src/components/common/ui/icon-button/IconButton.tsx new file mode 100644 index 0000000..f8a9b8b --- /dev/null +++ b/src/components/common/ui/icon-button/IconButton.tsx @@ -0,0 +1,45 @@ +import { FC, ReactNode } from 'react'; +import { PlusIcon } from '@heroicons/react/24/outline'; +import { + IconButton as IconButtonMUI, + IconButtonOwnProps as IconButtonPropsMUI, +} from '@mui/material'; +import { SxProps, Theme } from '@mui/material/styles'; + +import { + IconButtonSize, + IconButtonVariant, +} from '@/components/common/ui/icon-button/types'; +import mergeSx from '@/lib/utils/mergeSx'; + +import * as styles from './IconButton.styles'; + +interface IconButtonProps extends IconButtonPropsMUI { + children?: ReactNode; + size?: IconButtonSize; + variant?: IconButtonVariant; + onClick?: () => void; + sx?: SxProps; +} + +const IconButton: FC = ({ + children = , + size = 'medium', + variant = 'contained', + onClick = () => {}, + sx = {}, + ...props +}) => { + return ( + + {children} + + ); +}; + +export default IconButton; diff --git a/src/components/common/ui/icon-button/types/index.ts b/src/components/common/ui/icon-button/types/index.ts new file mode 100644 index 0000000..4eea48d --- /dev/null +++ b/src/components/common/ui/icon-button/types/index.ts @@ -0,0 +1,31 @@ +export type IconButtonSize = 'small' | 'medium'; + +export type IconButtonVariant = 'contained' | 'outlined'; + +export interface ButtonColors { + contained: Colors; + outlined: Colors; +} + +interface Colors { + default: { + color: string; + border: string; + }; + hover: { + color: string; + border: string; + }; + active: { + color: string; + border: string; + }; + focused: { + color: string; + border: string; + }; + disabled: { + color: string; + border: string; + }; +} diff --git a/src/components/common/ui/input/Input.styles.ts b/src/components/common/ui/input/Input.styles.ts index eebae1a..492c1ae 100644 --- a/src/components/common/ui/input/Input.styles.ts +++ b/src/components/common/ui/input/Input.styles.ts @@ -4,7 +4,6 @@ import { InputVariant } from '@/components/common/ui/input/types'; export const formControl = (variant: InputVariant): SxProps => ({ '.MuiInputBase-root': { - m: '10px 0 0 0', border: '2px solid', borderColor: 'gray.400', diff --git a/src/components/common/ui/input/Input.tsx b/src/components/common/ui/input/Input.tsx index f87496d..7906dd3 100644 --- a/src/components/common/ui/input/Input.tsx +++ b/src/components/common/ui/input/Input.tsx @@ -1,7 +1,11 @@ 'use client'; import React, { FC, useState } from 'react'; -import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'; +import { + EyeIcon, + EyeSlashIcon, + MagnifyingGlassIcon, +} from '@heroicons/react/24/outline'; import { FormControl, IconButton, @@ -28,6 +32,7 @@ interface InputProps extends InputPropsMUI { password?: boolean; sx?: SxProps; fullWidth?: boolean; + isSearch?: boolean; } const Input: FC = ({ @@ -39,6 +44,7 @@ const Input: FC = ({ password = false, sx = {}, fullWidth = false, + isSearch = false, ...props }) => { const [showPassword, setShowPassword] = useState(!password); @@ -47,16 +53,22 @@ const Input: FC = ({ setShowPassword((prevShowPassword) => !prevShowPassword); }; - const eyeColor = + const iconColor = variant === 'white' ? theme.palette.gray[300] : theme.palette.gray[400]; + const startAdornment = isSearch ? ( + + + + ) : null; + const endAdornment = password ? ( {showPassword ? ( - + ) : ( - + )} @@ -77,6 +89,7 @@ const Input: FC = ({ )} = { - width: '100%', - a: { - display: 'block', - p: '12px 20px', - borderRadius: '6px', - border: '1px solid', - borderColor: 'gray.500', - backgroundColor: 'gray.700', - boxShadow: '2px 2px 4px 0px rgba(0, 0, 0, 0.25)', - - '&:hover': { - backgroundColor: 'gray.600', - }, - - '&:focus': { - outline: '2px solid', - outlineColor: 'gray.100', - }, - }, -}; - -export const title: SxProps = { - display: 'flex', - justifyContent: 'space-between', -}; - -export const info: SxProps = { - display: 'grid', - gridTemplateColumns: 'auto 1fr', - columnGap: '20px', -}; - -export const avatar: SxProps = { - width: '64px', - height: '64px', - border: '1px solid', - borderColor: 'gray.500', -}; - -export const description: SxProps = { - mt: '14px', - overflow: 'hidden', - textOverflow: 'ellipsis', - display: '-webkit-box', - WebkitLineClamp: 3, - WebkitBoxOrient: 'vertical', -}; diff --git a/src/components/common/ui/organisation-card/OrganisationCard.tsx b/src/components/common/ui/organisation-card/OrganisationCard.tsx deleted file mode 100644 index 7f3fa2d..0000000 --- a/src/components/common/ui/organisation-card/OrganisationCard.tsx +++ /dev/null @@ -1,56 +0,0 @@ -'use client'; -import { FC } from 'react'; -import { Avatar, Box, Typography } from '@mui/material'; -import Link from 'next/link'; - -import Tag from '@/components/common/ui/tag'; -import useToast from '@/hooks/use-toast'; - -import RoundButtonIcon from '../round-button-icon'; - -import * as styles from './OrganisationCard.styles'; - -interface OrganisationCardProps { - avatar: string; - name: string; - position: string; - description?: string; -} - -const OrganisationCard: FC = ({ - avatar, - name, - position, - description = '', -}) => { - const toast = useToast(); - const handleDelete = () => { - toast.success('Deleted', '', 3000); - }; - - const href = 'organisation/1'; - - return ( - - - - - - - - {name} - - - - - - - {description && ( - {description} - )} - - - ); -}; - -export default OrganisationCard; diff --git a/src/components/common/ui/organisation-card/index.ts b/src/components/common/ui/organisation-card/index.ts deleted file mode 100644 index 0bee94d..0000000 --- a/src/components/common/ui/organisation-card/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './OrganisationCard'; diff --git a/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.styles.ts b/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.styles.ts new file mode 100644 index 0000000..19d0230 --- /dev/null +++ b/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.styles.ts @@ -0,0 +1,40 @@ +import { SxProps, Theme } from '@mui/material/styles'; + +export const modal: SxProps = { + zIndex: 10, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', +}; + +export const window: SxProps = { + zIndex: 10, + width: '500px', + backgroundColor: 'white.main', + px: '40px', + py: '20px', + borderRadius: '6px', + border: '1px solid', + borderColor: 'grey.500', + background: 'white.main', +}; + +export const backdrop: SxProps = { + background: 'rgba(151, 151, 151, 0.40);', + backdropFilter: 'blur(4px)', +}; + +export const avatarWrapper: SxProps = { + mt: '20px', + width: '160px', + height: '160px', + position: 'relative', +}; + +export const buttonIcon: SxProps = { + position: 'absolute', + right: '0px', + top: '120px', + margin: 'auto', +}; diff --git a/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.tsx b/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.tsx new file mode 100644 index 0000000..1202933 --- /dev/null +++ b/src/components/common/ui/pop-ups/organisation-popup/OrganisationPopup.tsx @@ -0,0 +1,155 @@ +import { FC, PropsWithChildren, useRef, useState } from 'react'; +import { PencilSquareIcon } from '@heroicons/react/24/outline'; +import { Avatar, Box, Modal, Stack, Typography } from '@mui/material'; +import { isAxiosError } from 'axios'; +import { useFormik } from 'formik'; + +import Button from '@/components/common/ui/button'; +import Input from '@/components/common/ui/input/Input'; +import RoundButtonIcon from '@/components/common/ui/round-button-icon'; +import useToast from '@/hooks/use-toast'; +import OrganisationsAPI from '@/lib/api/organisations/OrganisationsAPI'; + +import * as styles from './OrganisationPopup.styles'; + +interface OrganisationPopupProps { + open: boolean; + handleClick: () => void; +} + +const OrganisationPopup: FC> = ({ + children, + open, + handleClick, +}) => { + const toast = useToast(); + + const [avatar, setAvatar] = useState( + '/svgs/organisation-default.svg', + ); + const fileInputRef = useRef(null); + + const formik = useFormik({ + initialValues: { + avatar: null, + name: '', + address: '', + info: '', + }, + onSubmit: async (values) => { + console.log(values); + const formData = new FormData(); + if (values.avatar) { + formData.append('avatar', values.avatar as Blob); + } + formData.append('name', values.name); + formData.append('address', values.address); + if (values.info) { + formData.append('info', values.info); + } + + try { + await OrganisationsAPI.create(formData); + toast.success('Організація створена успішно'); + handleClick(); + } catch (error) { + if (isAxiosError(error)) { + toast.error( + 'Помилка при створенні організації', + error?.response?.data?.message, + ); + } + } + }, + }); + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.currentTarget.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setAvatar(reader.result as string); + formik.setFieldValue('avatar', file); + }; + reader.readAsDataURL(file); + } + }; + + const handleChangeAvatar = () => { + fileInputRef.current?.click(); + }; + + return ( + <> + {children} + + + + Створення організації +
+ + + + + } + onClick={handleChangeAvatar} + /> + + + + + + +
+
+
+
+ + ); +}; + +export default OrganisationPopup; diff --git a/src/components/common/ui/pop-ups/organisation-popup/types/index.ts b/src/components/common/ui/pop-ups/organisation-popup/types/index.ts new file mode 100644 index 0000000..ad512c8 --- /dev/null +++ b/src/components/common/ui/pop-ups/organisation-popup/types/index.ts @@ -0,0 +1,4 @@ +export interface ICreateOrganisationContext { + open: boolean; + handleClick: () => void; +} diff --git a/src/components/pages/NotFound.styles.ts b/src/components/pages/NotFound.styles.ts index f0d1971..1587a3f 100644 --- a/src/components/pages/NotFound.styles.ts +++ b/src/components/pages/NotFound.styles.ts @@ -1,16 +1,14 @@ import { SxProps, Theme } from '@mui/material/styles'; export const wrapper: SxProps = { - height: { - desktop: '100%', - mobile: '100vh', - }, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - m: '14px 24px 106px 24px', - + m: { + mobile: '106px 24px', + tablet: '14px 24px 106px 24px', + }, img: { maxWidth: '100%', height: 'auto', diff --git a/src/components/pages/auth-pages/login-page/LoginPage.tsx b/src/components/pages/auth-pages/login-page/LoginPage.tsx index 3d847bc..cc67745 100644 --- a/src/components/pages/auth-pages/login-page/LoginPage.tsx +++ b/src/components/pages/auth-pages/login-page/LoginPage.tsx @@ -14,7 +14,6 @@ import SideSection from '@/components/pages/auth-pages/components/side-section'; import useToast from '@/hooks/use-toast'; import AuthAPI from '@/lib/api/auth/AuthAPI'; import { exceptionMapper } from '@/lib/utils/exception-mapper'; -import storageUtil from '@/lib/utils/storageUtil'; import { initialValues } from './constants/initialValues'; import * as styles from './LoginPage.styles'; @@ -26,8 +25,7 @@ const LoginPage: FC = () => { initialValues, onSubmit: async (values) => { try { - const { accessToken } = await AuthAPI.login(values); - storageUtil.setToken(accessToken); + await AuthAPI.login(values); router.replace('/profile?tab=organisations'); return; } catch (e) { diff --git a/src/hooks/use-auth/useAuth.ts b/src/hooks/use-auth/useAuth.ts index 186ffa7..39f26b4 100644 --- a/src/hooks/use-auth/useAuth.ts +++ b/src/hooks/use-auth/useAuth.ts @@ -10,6 +10,8 @@ const useAuth = () => { mutate, } = useSWR('authorization', () => UserAPI.getUser(), { revalidateOnFocus: false, + refreshInterval: 0, + revalidateOnReconnect: false, }); return { diff --git a/src/lib/api/auth/AuthAPI.ts b/src/lib/api/auth/AuthAPI.ts index ac78343..9e488cf 100644 --- a/src/lib/api/auth/AuthAPI.ts +++ b/src/lib/api/auth/AuthAPI.ts @@ -2,7 +2,6 @@ import { ChangePasswordBody } from '@/lib/api/auth/types/ChangePasswordBody'; import { LoginBody } from '@/lib/api/auth/types/LoginBody'; import { RegisterBody } from '@/lib/api/auth/types/RegisterBody'; import { Token } from '@/lib/api/auth/types/Token'; -import { getAuthorizationHeader } from '@/lib/api/getAuthorizationHeader'; import { instance } from '@/lib/api/instance'; class AuthAPI { @@ -12,8 +11,7 @@ class AuthAPI { } async login(body: LoginBody) { - const { data } = await instance.post('/auth/login', body); - return data; + await instance.post('/auth/login', body); } async approveEmail(token: string) { @@ -38,11 +36,7 @@ class AuthAPI { } async changePassword(values: ChangePasswordBody) { - await instance.patch( - '/auth/change/password', - values, - getAuthorizationHeader(), - ); + await instance.patch('/auth/change/password', values); } } diff --git a/src/lib/api/getAuthorizationHeader.ts b/src/lib/api/getAuthorizationHeader.ts deleted file mode 100644 index 3a0eda0..0000000 --- a/src/lib/api/getAuthorizationHeader.ts +++ /dev/null @@ -1,9 +0,0 @@ -import storageUtil from '@/lib/utils/storageUtil'; - -export const getAuthorizationHeader = () => { - return { - headers: { - Authorization: `Bearer ${storageUtil.getAccessToken()}`, - }, - }; -}; diff --git a/src/lib/api/instance.ts b/src/lib/api/instance.ts index 9ca45a9..91e8f71 100644 --- a/src/lib/api/instance.ts +++ b/src/lib/api/instance.ts @@ -1,5 +1,8 @@ import axios from 'axios'; -const baseURL = process.env.BASE_URL; +const baseURL = process.env.NEXT_PUBLIC_API_URL; -export const instance = axios.create({ baseURL: 'http://localhost:80/' }); +export const instance = axios.create({ + baseURL, + withCredentials: true, +}); diff --git a/src/lib/api/organisations/OrganisationsAPI.ts b/src/lib/api/organisations/OrganisationsAPI.ts new file mode 100644 index 0000000..cc8d35c --- /dev/null +++ b/src/lib/api/organisations/OrganisationsAPI.ts @@ -0,0 +1,21 @@ +import { instance } from '@/lib/api/instance'; +import { IOrganisationResponse } from '@/lib/api/organisations/types'; + +class OrganisationsAPI { + async create(body: FormData) { + await instance.post('/organizations', body); + } + + async getById(id: string) { + const { data } = await instance.get( + `/organizations/${id}`, + ); + return data; + } + + async delete(id: string) { + await instance.delete(`/organizations/${id}`); + } +} + +export default new OrganisationsAPI(); diff --git a/src/lib/api/organisations/types/index.ts b/src/lib/api/organisations/types/index.ts new file mode 100644 index 0000000..b253075 --- /dev/null +++ b/src/lib/api/organisations/types/index.ts @@ -0,0 +1,6 @@ +export interface IOrganisationResponse { + id: string; + name: string; + avatar: string; + address: string; +} diff --git a/src/lib/api/user/UserAPI.ts b/src/lib/api/user/UserAPI.ts index 87ba72a..2ab66a2 100644 --- a/src/lib/api/user/UserAPI.ts +++ b/src/lib/api/user/UserAPI.ts @@ -1,29 +1,20 @@ -import { getAuthorizationHeader } from '@/lib/api/getAuthorizationHeader'; import { instance } from '@/lib/api/instance'; import { UserBody, UserInfo } from '@/lib/api/user/types/UserBody'; class UserAPI { async getUser() { - const { data } = await instance.get('/auth/user', { - ...getAuthorizationHeader(), - }); + const { data } = await instance.get('/auth/user'); return data; } async changePersonalInfo(info: UserInfo) { - const { data } = await instance.patch('/users', info, { - ...getAuthorizationHeader(), - }); + const { data } = await instance.patch('/users', info); return data; } async changeAvatar(body: FormData) { - const { data } = await instance.patch( - '/users/avatar', - body, - getAuthorizationHeader(), - ); + const { data } = await instance.patch('/users/avatar', body); return data; } } diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..1832542 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,12 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function middleware(request: NextRequest) { + const accessToken = request.cookies.get('accessToken'); + if (!accessToken) { + return NextResponse.redirect(new URL('/login', request.url)); + } +} + +export const config = { + matcher: ['/profile/:path*', '/organisations/:path*'], +}; diff --git a/src/types/auto.ts b/src/types/auto.ts new file mode 100644 index 0000000..15c093e --- /dev/null +++ b/src/types/auto.ts @@ -0,0 +1,10 @@ +export interface Auto { + id: number; + number: string; + brand: string; + model: string; + year: number; + vin: string; + engine: string; + volume: number; +}