diff --git a/apps/web/components/schedule/time-slots/components/edit-slot-form.tsx b/apps/web/components/schedule/time-slots/components/edit-slot-form.tsx deleted file mode 100644 index a5b5404..0000000 --- a/apps/web/components/schedule/time-slots/components/edit-slot-form.tsx +++ /dev/null @@ -1,65 +0,0 @@ -'use client'; -import { Context, ContextProvider } from '../context'; -import { type Props } from '../types'; -import { EditableTimePair, ReadonlyTimePair } from './time-pair'; -import { ScheduleSlotsContext } from '@/context/schedule-slots'; -import { useSlotDelete, useSlotMutation, useSlotQuery } from '@/hooks/slots'; -import { withContext } from '@/utils/context'; -import { combineDateTime } from '@/utils/date'; -import { Button } from '@repo/ui/components/ui/button'; -import { CircleX, Pencil, Save } from 'lucide-react'; -import { type FormEvent, use } from 'react'; - -export const EditSlotForm = withContext(ContextProvider)(function (props: Readonly) { - const { documentId } = props; - - const { editMode, endTime, setEditMode, setEndTime, setStartTime, startTime } = use(Context); - - const { selectedDate } = use(ScheduleSlotsContext); - - const { isLoading: isLoadingSlotQuery } = useSlotQuery({ documentId }); - const { isPending: isPendingSlotMutation, mutate: mutateSlot } = useSlotMutation({ documentId }); - const { isPending: isLoadingSlotDelete, mutate: deleteSlot } = useSlotDelete({ documentId }); - - const isPending = isPendingSlotMutation || isLoadingSlotQuery || isLoadingSlotDelete; - - const handleSubmit = (event: FormEvent) => { - event.preventDefault(); - if (startTime && endTime) { - mutateSlot({ - data: { - dateend: combineDateTime(selectedDate, endTime).toISOString(), - datestart: combineDateTime(selectedDate, startTime).toISOString(), - }, - documentId, - }); - setStartTime(''); - setEndTime(''); - } - - setEditMode(false); - }; - - const TimePair = editMode ? EditableTimePair : ReadonlyTimePair; - - return ( -
- - - - - - - ); -}); diff --git a/apps/web/components/schedule/time-slots/components/slot-card.tsx b/apps/web/components/schedule/time-slots/components/slot-card.tsx new file mode 100644 index 0000000..21a6a19 --- /dev/null +++ b/apps/web/components/schedule/time-slots/components/slot-card.tsx @@ -0,0 +1,35 @@ +'use client'; +import { ContextProvider } from '../context'; +import { type SlotProps } from '../types'; +import { ReadonlyTimePair } from './time-pair'; +import { useSlotQuery } from '@/hooks/slots'; +import { withContext } from '@/utils/context'; +import { Badge } from '@repo/ui/components/ui/badge'; +import Link from 'next/link'; + +export const SlotCard = withContext(ContextProvider)(function (props: Readonly) { + const { documentId } = props; + + const { data: slotData } = useSlotQuery({ documentId }); + if (!slotData?.slot) return null; + + const ordersNumber = slotData.slot.orders?.length; + const hasOrders = Boolean(ordersNumber); + + return ( + +
+ +
+ {hasOrders ? ( + Есть записи + ) : ( + + Свободно + + )} +
+
+ + ); +}); diff --git a/apps/web/components/schedule/time-slots/components/time-pair.tsx b/apps/web/components/schedule/time-slots/components/time-pair.tsx index 3e3b45b..d4a118f 100644 --- a/apps/web/components/schedule/time-slots/components/time-pair.tsx +++ b/apps/web/components/schedule/time-slots/components/time-pair.tsx @@ -1,11 +1,10 @@ 'use client'; import { Context, type ContextType } from '../context'; -import { type Props } from '../types'; -import { useSlotAdd, useSlotQuery } from '@/hooks/slots'; +import { type Slot } from '../types'; +import { useSlotAdd } from '@/hooks/slots'; import { getTimeString } from '@/utils/date'; import { Input } from '@repo/ui/components/ui/input'; -import { Loader } from 'lucide-react'; -import { use, useEffect } from 'react'; +import { use } from 'react'; type TimePairProps = Pick & { readonly disabled?: boolean; @@ -26,37 +25,13 @@ export function AddTimePair() { ); } -export function EditableTimePair({ documentId }: Readonly) { - const { editMode, endTime, setEndTime, setStartTime, startTime } = use(Context); - const { data } = useSlotQuery({ documentId }); - - useEffect(() => { - if (editMode) { - setStartTime(getTimeString(data?.slot?.datestart)); - setEndTime(getTimeString(data?.slot?.dateend)); - } - }, [data?.slot?.dateend, data?.slot?.datestart, editMode, setEndTime, setStartTime]); - +export function ReadonlyTimePair({ dateend, datestart }: Readonly) { return ( - - ); -} - -export function ReadonlyTimePair({ documentId }: Readonly) { - const { data } = useSlotQuery({ documentId }); - - if (!data) return ; - - return ( - <> - {getTimeString(data?.slot?.datestart)} - {getTimeString(data?.slot?.dateend)} - +
+ {getTimeString(datestart)} + {' - '} + {getTimeString(dateend)} +
); } diff --git a/apps/web/components/schedule/time-slots/index.tsx b/apps/web/components/schedule/time-slots/index.tsx index 6df5662..264568f 100644 --- a/apps/web/components/schedule/time-slots/index.tsx +++ b/apps/web/components/schedule/time-slots/index.tsx @@ -1,6 +1,6 @@ 'use client'; import { AddSlotForm } from './components/add-slot-form'; -import { EditSlotForm } from './components/edit-slot-form'; +import { SlotCard } from './components/slot-card'; import { useSlots } from '@/hooks/slots'; import { Loader } from 'lucide-react'; @@ -15,11 +15,11 @@ export function TimeSlots() { ); return ( -
+
{slots?.map((slot) => { if (!slot?.documentId) return null; - return ; + return ; })}
diff --git a/apps/web/components/schedule/time-slots/types.tsx b/apps/web/components/schedule/time-slots/types.tsx index 6945600..3c19d55 100644 --- a/apps/web/components/schedule/time-slots/types.tsx +++ b/apps/web/components/schedule/time-slots/types.tsx @@ -1,4 +1,5 @@ 'use client'; -import { type SlotFieldsFragment } from '@repo/graphql/types'; +import { type GetSlotQuery, type SlotFieldsFragment } from '@repo/graphql/types'; -export type Props = Pick; +export type Slot = NonNullable; +export type SlotProps = Pick; diff --git a/packages/graphql/operations/slot.graphql b/packages/graphql/operations/slot.graphql index 2c5faa8..3cdfa74 100644 --- a/packages/graphql/operations/slot.graphql +++ b/packages/graphql/operations/slot.graphql @@ -19,6 +19,9 @@ query GetSlots($filters: SlotFiltersInput) { query GetSlot($documentId: ID!) { slot(documentId: $documentId) { + orders { + documentId + } ...SlotFields } } diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index 8c6209e..a3bf91a 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -633,7 +633,7 @@ export type GetSlotQueryVariables = Exact<{ }>; -export type GetSlotQuery = { __typename?: 'Query', slot?: { __typename?: 'Slot', documentId: string, datestart: any, dateend: any, state?: Enum_Slot_State | null | undefined } | null | undefined }; +export type GetSlotQuery = { __typename?: 'Query', slot?: { __typename?: 'Slot', documentId: string, datestart: any, dateend: any, state?: Enum_Slot_State | null | undefined, orders: Array<{ __typename?: 'Order', documentId: string } | null | undefined> } | null | undefined }; export type UpdateSlotMutationVariables = Exact<{ documentId: Scalars['ID']['input']; @@ -661,6 +661,6 @@ export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"ki export const UpdateCustomerProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomerProfile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const CreateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSlot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datestart"}},{"kind":"Field","name":{"kind":"Name","value":"dateend"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} as unknown as DocumentNode; export const GetSlotsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlots"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datestart:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode; -export const GetSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datestart"}},{"kind":"Field","name":{"kind":"Name","value":"dateend"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} as unknown as DocumentNode; +export const GetSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datestart"}},{"kind":"Field","name":{"kind":"Name","value":"dateend"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} as unknown as DocumentNode; export const UpdateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSlot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datestart"}},{"kind":"Field","name":{"kind":"Name","value":"dateend"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} as unknown as DocumentNode; export const DeleteSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteSlot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/ui/src/components/ui/badge.tsx b/packages/ui/src/components/ui/badge.tsx new file mode 100644 index 0000000..e0e5a20 --- /dev/null +++ b/packages/ui/src/components/ui/badge.tsx @@ -0,0 +1,32 @@ +import { cn } from '@repo/ui/lib/utils'; +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; + +const badgeVariants = cva( + 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + { + defaultVariants: { + variant: 'default', + }, + variants: { + variant: { + default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', + destructive: + 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + outline: 'text-foreground', + secondary: + 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + success: 'border-transparent bg-success text-success-foreground hover:bg-success/80', + warning: 'border-transparent bg-warning text-warning-foreground hover:bg-warning/80', + }, + }, + }, +); + +export type BadgeProps = React.HTMLAttributes & VariantProps; + +function Badge({ className, variant, ...props }: BadgeProps) { + return
; +} + +export { Badge, badgeVariants }; diff --git a/packages/ui/src/globals.css b/packages/ui/src/globals.css index f7c0d53..113c2a4 100644 --- a/packages/ui/src/globals.css +++ b/packages/ui/src/globals.css @@ -30,6 +30,10 @@ --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + --warning: 31 96% 53%; + --warning-foreground: 0 0% 98%; + --success: 100 77% 44%; + --success-foreground: 0 0% 98%; } .dark { @@ -58,6 +62,10 @@ --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; + --warning: 31 96% 53%; + --warning-foreground: 0 0% 98%; + --success: 100 77% 33%; + --success-foreground: 0 0% 98%; } } diff --git a/packages/ui/tailwind.config.ts b/packages/ui/tailwind.config.ts index 7c11487..2a757a2 100644 --- a/packages/ui/tailwind.config.ts +++ b/packages/ui/tailwind.config.ts @@ -65,6 +65,14 @@ const config = { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))', }, + success: { + DEFAULT: 'hsl(var(--success))', + foreground: 'hsl(var(--success-foreground))', + }, + warning: { + DEFAULT: 'hsl(var(--warning))', + foreground: 'hsl(var(--warning-foreground))', + }, }, keyframes: { 'accordion-down': {