create slot cards
This commit is contained in:
parent
09627a5336
commit
fbf93b3ee0
@ -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<Props>) {
|
||||
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 (
|
||||
<form
|
||||
className="grid grid-cols-[auto_1fr_1fr_auto_auto] items-center gap-2"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<span className="text-base font-bold">•</span>
|
||||
<TimePair {...props} />
|
||||
|
||||
<Button
|
||||
disabled={isPending}
|
||||
onClick={editMode ? undefined : () => setEditMode(true)}
|
||||
type={editMode ? 'submit' : 'button'}
|
||||
>
|
||||
{editMode ? <Save className="size-4" /> : <Pencil className="size-4" />}
|
||||
</Button>
|
||||
<Button disabled={isPending} onClick={() => deleteSlot()}>
|
||||
<CircleX className="size-4" />
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
});
|
||||
@ -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<SlotProps>) {
|
||||
const { documentId } = props;
|
||||
|
||||
const { data: slotData } = useSlotQuery({ documentId });
|
||||
if (!slotData?.slot) return null;
|
||||
|
||||
const ordersNumber = slotData.slot.orders?.length;
|
||||
const hasOrders = Boolean(ordersNumber);
|
||||
|
||||
return (
|
||||
<Link href={`/profile/schedule/slot/${documentId}`} rel="noopener noreferrer">
|
||||
<div className="flex justify-between rounded-2xl bg-background p-4 px-6 dark:bg-primary/5">
|
||||
<ReadonlyTimePair {...slotData.slot} />
|
||||
<div>
|
||||
{hasOrders ? (
|
||||
<Badge variant="success">Есть записи</Badge>
|
||||
) : (
|
||||
<Badge className="bg-transparent text-xs font-normal text-muted-foreground">
|
||||
Свободно
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
@ -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<ContextType, 'endTime' | 'setEndTime' | 'setStartTime' | 'startTime'> & {
|
||||
readonly disabled?: boolean;
|
||||
@ -26,37 +25,13 @@ export function AddTimePair() {
|
||||
);
|
||||
}
|
||||
|
||||
export function EditableTimePair({ documentId }: Readonly<Props>) {
|
||||
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<Slot>) {
|
||||
return (
|
||||
<TimeInputPair
|
||||
endTime={endTime}
|
||||
setEndTime={setEndTime}
|
||||
setStartTime={setStartTime}
|
||||
startTime={startTime}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReadonlyTimePair({ documentId }: Readonly<Props>) {
|
||||
const { data } = useSlotQuery({ documentId });
|
||||
|
||||
if (!data) return <Loader className="animate-spin" />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="p-1 text-lg font-bold">{getTimeString(data?.slot?.datestart)}</span>
|
||||
<span className="p-1 text-lg font-bold">{getTimeString(data?.slot?.dateend)}</span>
|
||||
</>
|
||||
<div className="flex flex-row items-center gap-2 text-lg font-bold">
|
||||
<span>{getTimeString(datestart)}</span>
|
||||
{' - '}
|
||||
<span>{getTimeString(dateend)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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 (
|
||||
<div className="space-y-2 p-4">
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
{slots?.map((slot) => {
|
||||
if (!slot?.documentId) return null;
|
||||
|
||||
return <EditSlotForm key={slot.documentId} {...slot} />;
|
||||
return <SlotCard key={slot.documentId} {...slot} />;
|
||||
})}
|
||||
<AddSlotForm />
|
||||
</div>
|
||||
|
||||
@ -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<SlotFieldsFragment, 'documentId'>;
|
||||
export type Slot = NonNullable<GetSlotQuery['slot']>;
|
||||
export type SlotProps = Pick<SlotFieldsFragment, 'documentId'>;
|
||||
|
||||
@ -19,6 +19,9 @@ query GetSlots($filters: SlotFiltersInput) {
|
||||
|
||||
query GetSlot($documentId: ID!) {
|
||||
slot(documentId: $documentId) {
|
||||
orders {
|
||||
documentId
|
||||
}
|
||||
...SlotFields
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<UpdateCustomerProfileMutation, UpdateCustomerProfileMutationVariables>;
|
||||
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<CreateSlotMutation, CreateSlotMutationVariables>;
|
||||
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<GetSlotsQuery, GetSlotsQueryVariables>;
|
||||
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<GetSlotQuery, GetSlotQueryVariables>;
|
||||
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<GetSlotQuery, GetSlotQueryVariables>;
|
||||
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<UpdateSlotMutation, UpdateSlotMutationVariables>;
|
||||
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<DeleteSlotMutation, DeleteSlotMutationVariables>;
|
||||
32
packages/ui/src/components/ui/badge.tsx
Normal file
32
packages/ui/src/components/ui/badge.tsx
Normal file
@ -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<HTMLDivElement> & VariantProps<typeof badgeVariants>;
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||
@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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': {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user