Compare commits
No commits in common. '057e9c904339b90358b88b6ff557cf4618e4816d' and 'a6d03971eb14ec079375e16b22e57351d9a4274a' have entirely different histories.
057e9c9043
...
a6d03971eb
@ -1,20 +0,0 @@ |
|||||||
'use client' |
|
||||||
import CommonContainer from "@/components/(dashboard)/elements/CommonContainer" |
|
||||||
import AppContextProvider from "@/helpers/context/AppContextProvider" |
|
||||||
import AdminView from "@/views/AdminView" |
|
||||||
|
|
||||||
const DashBoardIndexPage = () => { |
|
||||||
return( |
|
||||||
<> |
|
||||||
<AppContextProvider> |
|
||||||
<AdminView> |
|
||||||
<CommonContainer> |
|
||||||
<h2>hello</h2> |
|
||||||
</CommonContainer> |
|
||||||
</AdminView> |
|
||||||
</AppContextProvider> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default DashBoardIndexPage |
|
@ -1,22 +0,0 @@ |
|||||||
import { Toaster } from "@/components/(dashboard)/ui/toaster" |
|
||||||
import AppContextProvider from "@/helpers/context/AppContextProvider" |
|
||||||
import { Suspense } from "react" |
|
||||||
|
|
||||||
const RootLayout :React.FC<{ |
|
||||||
children :React.ReactNode |
|
||||||
}> = ({ |
|
||||||
children |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<> |
|
||||||
<Suspense> |
|
||||||
<AppContextProvider> |
|
||||||
{children} |
|
||||||
</AppContextProvider> |
|
||||||
<Toaster /> |
|
||||||
</Suspense> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default RootLayout |
|
@ -1,195 +0,0 @@ |
|||||||
import React, { useState } from 'react'; |
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; |
|
||||||
import { Label } from "@/components/ui/label"; |
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"; |
|
||||||
import { Separator } from "@/components/ui/separator"; |
|
||||||
import { Check, X } from "lucide-react"; |
|
||||||
|
|
||||||
const QuizGenerator = () => { |
|
||||||
// Submitted questions history
|
|
||||||
const [submittedQuestions] = useState([ |
|
||||||
{ |
|
||||||
id: 1, |
|
||||||
question: "What is React's Virtual DOM?", |
|
||||||
options: [ |
|
||||||
"A complete DOM copy", |
|
||||||
"A lightweight copy of the DOM", |
|
||||||
"A browser feature", |
|
||||||
"A routing system" |
|
||||||
], |
|
||||||
selectedOption: 0, // User selected
|
|
||||||
correctOption: 1, // Correct answer
|
|
||||||
}, |
|
||||||
{ |
|
||||||
id: 2, |
|
||||||
question: "What hook manages side effects?", |
|
||||||
options: [ |
|
||||||
"useState", |
|
||||||
"useReducer", |
|
||||||
"useEffect", |
|
||||||
"useContext" |
|
||||||
], |
|
||||||
selectedOption: 2, |
|
||||||
correctOption: 2, |
|
||||||
}, |
|
||||||
]); |
|
||||||
|
|
||||||
// Unsubmitted questions
|
|
||||||
const [pendingQuestions, setPendingQuestions] = useState([ |
|
||||||
{ |
|
||||||
id: 1, |
|
||||||
question: "Which of these is a state management library?", |
|
||||||
options: ["Redux", "Axios", "Lodash", "Moment"], |
|
||||||
selectedOption: null |
|
||||||
}, |
|
||||||
{ |
|
||||||
id: 2, |
|
||||||
question: "What does CSS stand for?", |
|
||||||
options: [ |
|
||||||
"Computer Style Sheets", |
|
||||||
"Cascading Style Sheets", |
|
||||||
"Creative Style Sheets", |
|
||||||
"Colorful Style Sheets" |
|
||||||
], |
|
||||||
selectedOption: null |
|
||||||
}, |
|
||||||
]); |
|
||||||
|
|
||||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); |
|
||||||
|
|
||||||
const handleOptionSelect = (value) => { |
|
||||||
const updatedQuestions = [...pendingQuestions]; |
|
||||||
updatedQuestions[currentQuestionIndex].selectedOption = parseInt(value); |
|
||||||
setPendingQuestions(updatedQuestions); |
|
||||||
}; |
|
||||||
|
|
||||||
const generateQuiz = () => { |
|
||||||
// Reset selections and shuffle pending questions
|
|
||||||
const shuffledQuestions = [...pendingQuestions].map(q => ({ |
|
||||||
...q, |
|
||||||
selectedOption: null |
|
||||||
})); |
|
||||||
setPendingQuestions(shuffledQuestions); |
|
||||||
setCurrentQuestionIndex(0); |
|
||||||
}; |
|
||||||
|
|
||||||
const handleNext = () => { |
|
||||||
if (currentQuestionIndex < pendingQuestions.length - 1) { |
|
||||||
setCurrentQuestionIndex(prev => prev + 1); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
const handlePrevious = () => { |
|
||||||
if (currentQuestionIndex > 0) { |
|
||||||
setCurrentQuestionIndex(prev => prev - 1); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className="py-6 container mx- mt-8"> |
|
||||||
{/* Submitted Questions History */} |
|
||||||
<div className='grid grid-cols-3 space-x-6'> |
|
||||||
<Card className="w-full col-span-2"> |
|
||||||
<CardHeader> |
|
||||||
<CardTitle>Quiz History</CardTitle> |
|
||||||
</CardHeader> |
|
||||||
<CardContent> |
|
||||||
<ScrollArea className="h-[400px] pr-4"> |
|
||||||
{submittedQuestions.map((q, index) => ( |
|
||||||
<div key={q.id} className="mb-4"> |
|
||||||
<div className="p-4 border rounded-lg border-gray-200"> |
|
||||||
<h3 className="font-medium mb-2">{q.question}</h3> |
|
||||||
<div className="text-sm space-y-2"> |
|
||||||
{q.options.map((option, i) => ( |
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className={`p-2 rounded flex justify-between items-center ${ |
|
||||||
i === q.correctOption && i === q.selectedOption |
|
||||||
? 'bg-green-100' |
|
||||||
: i === q.correctOption |
|
||||||
? 'bg-green-50' |
|
||||||
: i === q.selectedOption |
|
||||||
? 'bg-red-50' |
|
||||||
: 'bg-gray-50' |
|
||||||
}`}
|
|
||||||
> |
|
||||||
<span>{option}</span> |
|
||||||
{i === q.correctOption && ( |
|
||||||
<Check className="w-4 h-4 text-green-600" /> |
|
||||||
)} |
|
||||||
{i === q.selectedOption && i !== q.correctOption && ( |
|
||||||
<X className="w-4 h-4 text-red-600" /> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
{index < submittedQuestions.length - 1 && <Separator className="my-4" />} |
|
||||||
</div> |
|
||||||
))} |
|
||||||
</ScrollArea> |
|
||||||
</CardContent> |
|
||||||
</Card> |
|
||||||
|
|
||||||
{/* Pending Questions Interface */} |
|
||||||
<div className="w-full space-y-6"> |
|
||||||
<Button
|
|
||||||
onClick={generateQuiz} |
|
||||||
className="w-full bg-purple-700" |
|
||||||
> |
|
||||||
Generate Quiz |
|
||||||
</Button> |
|
||||||
|
|
||||||
{pendingQuestions.length > 0 ? ( |
|
||||||
<Card> |
|
||||||
<CardHeader> |
|
||||||
<CardTitle> |
|
||||||
Question {currentQuestionIndex + 1} of {pendingQuestions.length} |
|
||||||
</CardTitle> |
|
||||||
</CardHeader> |
|
||||||
<CardContent> |
|
||||||
<div className="space-y-6"> |
|
||||||
<h3 className="text-lg font-medium"> |
|
||||||
{pendingQuestions[currentQuestionIndex].question} |
|
||||||
</h3> |
|
||||||
|
|
||||||
<RadioGroup |
|
||||||
value={pendingQuestions[currentQuestionIndex].selectedOption?.toString()} |
|
||||||
onValueChange={handleOptionSelect} |
|
||||||
> |
|
||||||
{pendingQuestions[currentQuestionIndex].options.map((option, i) => ( |
|
||||||
<div key={i} className="flex items-center space-x-2"> |
|
||||||
<RadioGroupItem value={i.toString()} id={`option-${i}`} /> |
|
||||||
<Label htmlFor={`option-${i}`}>{option}</Label> |
|
||||||
</div> |
|
||||||
))} |
|
||||||
</RadioGroup> |
|
||||||
|
|
||||||
<div className="flex justify-end mt-6"> |
|
||||||
<Button
|
|
||||||
onClick={handleNext} |
|
||||||
disabled={currentQuestionIndex === pendingQuestions.length - 1} |
|
||||||
className='bg-purple-700' |
|
||||||
> |
|
||||||
Submit |
|
||||||
</Button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</CardContent> |
|
||||||
</Card> |
|
||||||
) : ( |
|
||||||
<Card> |
|
||||||
<CardContent className="p-6 text-center text-gray-500"> |
|
||||||
No pending questions available |
|
||||||
</CardContent> |
|
||||||
</Card> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default QuizGenerator; |
|
@ -1,169 +0,0 @@ |
|||||||
import AsyncSelect from 'react-select/async'; |
|
||||||
import React, { useEffect, useRef } from 'react'; |
|
||||||
import { ChoiceType } from '@/helpers/commonSchema/common.schema'; |
|
||||||
import { Label } from '../ui/label'; |
|
||||||
import { StylesConfig } from 'node_modules/react-select/dist/declarations/src'; |
|
||||||
|
|
||||||
interface SelectFieldProps { |
|
||||||
className?: string; |
|
||||||
id?: string; |
|
||||||
label?: string; |
|
||||||
labelStyle: string; |
|
||||||
labelWidth?: string; |
|
||||||
name: string; |
|
||||||
defaultValue?: ChoiceType | null; |
|
||||||
value?: string | number | boolean; |
|
||||||
required?: boolean; |
|
||||||
wrapperClassName?: string; |
|
||||||
options?: { value: string | number; label: string }[]; |
|
||||||
fieldErrors?: Array<string>; |
|
||||||
clearSelectValue?: boolean; |
|
||||||
setClearSelectValue?: React.Dispatch<React.SetStateAction<boolean>>; |
|
||||||
isDisabled?: boolean; |
|
||||||
onChange?: (choice: ChoiceType) => void | any; |
|
||||||
loadOptions? : (inputValue: string, callback: (options: any[]) => void) => Promise<void> | any |
|
||||||
placeHolder? : string |
|
||||||
labelClassName? : string |
|
||||||
defaultOptions? : { value: string | number; label: string }[] |
|
||||||
isMulti? : boolean |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const AsyncSelectField: React.FC<SelectFieldProps> = ({ |
|
||||||
className = '', |
|
||||||
id, |
|
||||||
label, |
|
||||||
labelStyle, |
|
||||||
labelWidth, |
|
||||||
name, |
|
||||||
defaultValue, |
|
||||||
value, |
|
||||||
required, |
|
||||||
wrapperClassName = '', |
|
||||||
fieldErrors = [], |
|
||||||
clearSelectValue = false, |
|
||||||
setClearSelectValue, |
|
||||||
isDisabled, |
|
||||||
onChange, |
|
||||||
loadOptions, |
|
||||||
placeHolder, |
|
||||||
labelClassName , |
|
||||||
defaultOptions , |
|
||||||
isMulti = false , |
|
||||||
}) => { |
|
||||||
if (labelStyle === 'label-left') { |
|
||||||
wrapperClassName = wrapperClassName.concat( |
|
||||||
' ', |
|
||||||
' flex justify-between items-center gap-4 ' |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
const selectRef = useRef<any>(null); |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
if (clearSelectValue) { |
|
||||||
selectRef?.current?.clearValue?.(); |
|
||||||
setClearSelectValue && setClearSelectValue(false); |
|
||||||
} |
|
||||||
}, [clearSelectValue, setClearSelectValue]); |
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// // Auto-select the first option if available
|
|
||||||
// if (!selectedValue && defaultOptions && defaultOptions.length > 0) {
|
|
||||||
// const firstOption = defaultOptions[0];
|
|
||||||
// setSelectedValue(firstOption);
|
|
||||||
// onChange && onChange(firstOption);
|
|
||||||
// }
|
|
||||||
// }, [defaultOptions, selectedValue, onChange]);
|
|
||||||
|
|
||||||
const styles = { |
|
||||||
menuList: (base: any , state :any) => ({ |
|
||||||
...base, |
|
||||||
borderColor : state?.isFocused && 'bg-[var(--primary)]', |
|
||||||
colourStyles : '#fff', |
|
||||||
'::-webkit-scrollbar': { |
|
||||||
width: '5px', |
|
||||||
}, |
|
||||||
'::-webkit-scrollbar-track': { |
|
||||||
background: '#f1f1f1', |
|
||||||
}, |
|
||||||
'::-webkit-scrollbar-thumb': { |
|
||||||
background: '#d1d1d1', |
|
||||||
}, |
|
||||||
'::-webkit-scrollbar-thumb:hover': { |
|
||||||
background: '#555', |
|
||||||
}, |
|
||||||
}), |
|
||||||
input: (base:any, state : any) => ({ |
|
||||||
...base, |
|
||||||
'[type="text"]': { |
|
||||||
fontSize: 13, |
|
||||||
fontWeight: 900, |
|
||||||
color: 'green' |
|
||||||
} |
|
||||||
}) |
|
||||||
}; |
|
||||||
|
|
||||||
const colourStyles: StylesConfig<any> = { |
|
||||||
control: (styles) => ({ ...styles, backgroundColor: 'white' }), |
|
||||||
option: (styles, { data, isDisabled, isFocused, isSelected }) => { |
|
||||||
return { |
|
||||||
...styles, |
|
||||||
backgroundColor:'hsl(var(--background))', |
|
||||||
color:'#fff' |
|
||||||
}; |
|
||||||
}, |
|
||||||
|
|
||||||
// input: (styles) => ({ ...styles, ...dot() }),
|
|
||||||
// placeholder: (styles) => ({ ...styles, ...dot('#ccc') }),
|
|
||||||
// singleValue: (styles, { data }) => ({ ...styles, ...dot(data.color) }),
|
|
||||||
};
|
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={`${wrapperClassName}`}> |
|
||||||
{label && ( |
|
||||||
<Label className={`${labelClassName} ${fieldErrors?.length > 0 && 'text-destructive'}`}> |
|
||||||
{label} |
|
||||||
{required ? <span className="text-red-400"> *</span> : ''} |
|
||||||
</Label> |
|
||||||
)} |
|
||||||
<div className="w-full mt-2"> |
|
||||||
<AsyncSelect |
|
||||||
cacheOptions |
|
||||||
isMulti={isMulti} |
|
||||||
loadOptions={loadOptions} |
|
||||||
defaultOptions={defaultOptions} |
|
||||||
ref={selectRef} |
|
||||||
styles={styles} |
|
||||||
isDisabled={isDisabled} |
|
||||||
onChange={onChange as any} |
|
||||||
className={className + 'bg-[var(--primary)]' + ' !text-sm '+ (fieldErrors?.length > 0 && 'border border-destructive')} |
|
||||||
theme={(theme) => ({ |
|
||||||
...theme, |
|
||||||
borderRadius: 0, |
|
||||||
colors: { |
|
||||||
...theme.colors, |
|
||||||
backgroundColor: 'hsl(var(--background))' , |
|
||||||
primary25: 'hsl(var(--secondary))', |
|
||||||
primary: 'hsl(var(--secondary))', |
|
||||||
color : '#fff' |
|
||||||
}, |
|
||||||
})} |
|
||||||
|
|
||||||
id={id} |
|
||||||
name={name} |
|
||||||
defaultValue={defaultValue} |
|
||||||
placeholder={placeHolder} |
|
||||||
|
|
||||||
/> |
|
||||||
<span className="block text-destructive text-xs my-1"> |
|
||||||
{fieldErrors.map((item: string, index: any) => ( |
|
||||||
<span key={index}>{item}</span> |
|
||||||
))} |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default AsyncSelectField; |
|
@ -1,174 +0,0 @@ |
|||||||
import React, { useCallback, useState, useEffect } from 'react'; |
|
||||||
import { useDropzone } from 'react-dropzone'; |
|
||||||
import { Label } from '../ui/label'; |
|
||||||
import { FolderInput, Trash2 } from 'lucide-react'; |
|
||||||
|
|
||||||
interface FileWithPreview extends File { |
|
||||||
preview: string; |
|
||||||
} |
|
||||||
|
|
||||||
interface InitialFileState { |
|
||||||
file: FileWithPreview | null; |
|
||||||
path: string | null; |
|
||||||
} |
|
||||||
|
|
||||||
interface DropZonePropsInterface { |
|
||||||
title?: string; |
|
||||||
handleFileChange: (key: string, file: File | null) => void; |
|
||||||
name: string; |
|
||||||
initialItem?: File | string | null; |
|
||||||
currentFile?: File | null; // Add this prop
|
|
||||||
} |
|
||||||
|
|
||||||
const DropZone: React.FC<DropZonePropsInterface> = ({ |
|
||||||
title = 'Upload File', |
|
||||||
handleFileChange, |
|
||||||
name, |
|
||||||
initialItem = null, |
|
||||||
currentFile = null, // Add this prop
|
|
||||||
}) => { |
|
||||||
const [fileState, setFileState] = useState<InitialFileState>({ |
|
||||||
file: null, |
|
||||||
path: typeof initialItem === 'string' ? initialItem : null |
|
||||||
}); |
|
||||||
|
|
||||||
// Effect to handle initial setup and updates
|
|
||||||
useEffect(() => { |
|
||||||
if (currentFile) { |
|
||||||
// If there's a current file in form state, create preview
|
|
||||||
const fileWithPreview = Object.assign(currentFile, { |
|
||||||
preview: URL.createObjectURL(currentFile) |
|
||||||
}) as FileWithPreview; |
|
||||||
setFileState({ file: fileWithPreview, path: null }); |
|
||||||
} else if (initialItem && typeof initialItem === 'string') { |
|
||||||
// Handle path-based initialItem
|
|
||||||
setFileState(prev => ({ ...prev, path: initialItem })); |
|
||||||
} else if (initialItem instanceof File) { |
|
||||||
// Handle File-based initialItem
|
|
||||||
const fileWithPreview = Object.assign(initialItem, { |
|
||||||
preview: URL.createObjectURL(initialItem) |
|
||||||
}) as FileWithPreview; |
|
||||||
setFileState({ file: fileWithPreview, path: null }); |
|
||||||
} else if (!initialItem && !currentFile) { |
|
||||||
// Reset state if no file
|
|
||||||
setFileState({ file: null, path: null }); |
|
||||||
} |
|
||||||
}, [initialItem, currentFile]); |
|
||||||
|
|
||||||
const onDrop = useCallback( |
|
||||||
(acceptedFiles: File[]) => { |
|
||||||
if (acceptedFiles.length > 0) { |
|
||||||
const fileWithPreview = Object.assign(acceptedFiles[0], { |
|
||||||
preview: URL.createObjectURL(acceptedFiles[0]), |
|
||||||
}) as FileWithPreview; |
|
||||||
|
|
||||||
setFileState({ file: fileWithPreview, path: null }); |
|
||||||
handleFileChange(name, fileWithPreview); |
|
||||||
} |
|
||||||
}, |
|
||||||
[name, handleFileChange] |
|
||||||
); |
|
||||||
|
|
||||||
const removeFile = useCallback((e: React.MouseEvent) => { |
|
||||||
e.stopPropagation(); |
|
||||||
setFileState({ file: null, path: null }); |
|
||||||
handleFileChange(name, null); |
|
||||||
}, [name, handleFileChange]); |
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({ |
|
||||||
onDrop, |
|
||||||
accept: { |
|
||||||
'image/jpeg': ['.jpeg', '.jpg'], |
|
||||||
'image/png': ['.png'], |
|
||||||
'application/pdf': ['.pdf'], |
|
||||||
'application/msword': ['.doc', '.docx'] |
|
||||||
}, |
|
||||||
multiple: false |
|
||||||
}); |
|
||||||
|
|
||||||
const renderPreview = () => { |
|
||||||
const currentFile = fileState.file; |
|
||||||
const currentPath = fileState.path; |
|
||||||
|
|
||||||
if (currentFile) { |
|
||||||
if (currentFile.type.startsWith('image/')) { |
|
||||||
return ( |
|
||||||
<div className="mt-4 flex flex-col items-center gap-2"> |
|
||||||
<img |
|
||||||
src={currentFile.preview} |
|
||||||
alt={currentFile.name} |
|
||||||
className="max-h-32 border rounded" |
|
||||||
/> |
|
||||||
<div className="flex items-center gap-2"> |
|
||||||
<FolderInput className="w-4 h-4" /> |
|
||||||
<span className="text-sm">{currentFile.name}</span> |
|
||||||
<button |
|
||||||
type="button" |
|
||||||
onClick={removeFile} |
|
||||||
className="hover:bg-gray-200 p-1 rounded-full" |
|
||||||
aria-label="Remove file" |
|
||||||
> |
|
||||||
<Trash2 className="w-4 h-4 text-red-500" /> |
|
||||||
</button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
} else if (currentPath) { |
|
||||||
if (currentPath.match(/\.(jpg|jpeg|png)$/i)) { |
|
||||||
return ( |
|
||||||
<div className="mt-4 flex flex-col items-center gap-2"> |
|
||||||
<img |
|
||||||
src={currentPath} |
|
||||||
alt="Uploaded file" |
|
||||||
className="max-h-32 border rounded" |
|
||||||
/> |
|
||||||
<div className="flex items-center gap-2"> |
|
||||||
<FolderInput className="w-4 h-4" /> |
|
||||||
<span className="text-sm">{currentPath.split('/').pop()}</span> |
|
||||||
<button |
|
||||||
type="button" |
|
||||||
onClick={removeFile} |
|
||||||
className="hover:bg-gray-200 p-1 rounded-full" |
|
||||||
aria-label="Remove file" |
|
||||||
> |
|
||||||
<Trash2 className="w-4 h-4 text-red-500" /> |
|
||||||
</button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
}; |
|
||||||
|
|
||||||
// Cleanup preview URLs on unmount
|
|
||||||
useEffect(() => { |
|
||||||
return () => { |
|
||||||
if (fileState.file?.preview) { |
|
||||||
URL.revokeObjectURL(fileState.file.preview); |
|
||||||
} |
|
||||||
}; |
|
||||||
}, [fileState.file]); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className="flex flex-col gap-4"> |
|
||||||
<Label>{title}</Label> |
|
||||||
<div |
|
||||||
{...getRootProps()} |
|
||||||
className="bg-sky-50 dark:bg-background border-gray-400 border border-dashed px-4 py-8 rounded cursor-pointer" |
|
||||||
> |
|
||||||
<input {...getInputProps()} /> |
|
||||||
<div className="text-center"> |
|
||||||
<p className="text-sm">Drag and drop a file here, or click to select a file</p> |
|
||||||
<em className="text-sm block mt-1">(*.jpeg, *.png, *.pdf, *.doc files accepted)</em> |
|
||||||
</div> |
|
||||||
|
|
||||||
{renderPreview()} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default DropZone; |
|
@ -1,121 +0,0 @@ |
|||||||
import React, { useContext } from "react"; |
|
||||||
import { Label } from "@/components/ui/label"; |
|
||||||
import { Input } from "@/components/ui/input"; |
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { useTheme } from "@/hooks/use-theme"; |
|
||||||
|
|
||||||
interface FormInputProps extends React.InputHTMLAttributes<HTMLInputElement> { |
|
||||||
label?: string; |
|
||||||
error?: []; |
|
||||||
labelClassName?: string; |
|
||||||
containerClassName?: string; |
|
||||||
helperText?: string; |
|
||||||
labelPosition?: "top" | "left"; |
|
||||||
required? : boolean ,
|
|
||||||
name : string |
|
||||||
type?: |
|
||||||
| "textarea" |
|
||||||
| "text" |
|
||||||
| "file" |
|
||||||
| "email" |
|
||||||
| "tel" |
|
||||||
| "password" |
|
||||||
| "number" |
|
||||||
| "search" |
|
||||||
| "color" |
|
||||||
| "date" |
|
||||||
| "time" |
|
||||||
| "range" |
|
||||||
| "datetime-local" |
|
||||||
| "checkbox"; |
|
||||||
} |
|
||||||
|
|
||||||
const FormInput = React.forwardRef<HTMLInputElement, FormInputProps>( |
|
||||||
( |
|
||||||
{ |
|
||||||
label, |
|
||||||
error, |
|
||||||
name , |
|
||||||
className, |
|
||||||
labelClassName, |
|
||||||
containerClassName, |
|
||||||
helperText, |
|
||||||
labelPosition = "top", |
|
||||||
id, |
|
||||||
type = "text" ,
|
|
||||||
required = false , |
|
||||||
...props |
|
||||||
}, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const inputId = id || label?.toLowerCase().replace(/\s+/g, "-"); |
|
||||||
const {theme} = useTheme() |
|
||||||
|
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={cn( |
|
||||||
"w-full", |
|
||||||
labelPosition === "left" && "grid grid-cols-4 gap-4 items-center", |
|
||||||
containerClassName |
|
||||||
)}> |
|
||||||
{label && ( |
|
||||||
<Label |
|
||||||
htmlFor={inputId} |
|
||||||
className={cn( |
|
||||||
labelPosition === "left" && "text-right", |
|
||||||
error && "text-destructive", |
|
||||||
labelClassName, |
|
||||||
'font-medium' |
|
||||||
)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
{required ? <span className="text-red-400"> *</span> : ""} |
|
||||||
</Label> |
|
||||||
)} |
|
||||||
<div className={cn( |
|
||||||
"flex flex-col gap-1.5", |
|
||||||
labelPosition === "left" && "col-span-3", |
|
||||||
labelPosition !== "left" && 'mt-2' |
|
||||||
)}> |
|
||||||
<Input |
|
||||||
id={inputId} |
|
||||||
ref={ref} |
|
||||||
name={name} |
|
||||||
type={type} |
|
||||||
className={cn( |
|
||||||
error && "border-destructive", |
|
||||||
className ,
|
|
||||||
type === 'date' && theme, |
|
||||||
'py-[20px]' |
|
||||||
)} |
|
||||||
aria-describedby={ |
|
||||||
error ? `${inputId}-error` :
|
|
||||||
helperText ? `${inputId}-helper` :
|
|
||||||
undefined |
|
||||||
} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
{helperText && ( |
|
||||||
<p |
|
||||||
id={`${inputId}-helper`} |
|
||||||
className="text-sm text-muted-foreground" |
|
||||||
> |
|
||||||
{helperText} |
|
||||||
</p> |
|
||||||
)} |
|
||||||
{error?.map((item: string, index: any) => ( |
|
||||||
<span key={index} className="text-sm text-destructive"> |
|
||||||
{item} |
|
||||||
<br></br> |
|
||||||
</span> |
|
||||||
))} |
|
||||||
|
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
); |
|
||||||
|
|
||||||
FormInput.displayName = "FormInput"; |
|
||||||
|
|
||||||
export default FormInput; |
|
@ -1,47 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import React from "react"; |
|
||||||
import Stepper from "@/components/ui/stepper"; |
|
||||||
import { Step } from "@/helpers/commonSchema/steps.schema"; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function MultiStepForm({steps , renderContent , className} :
|
|
||||||
{steps : Step[] , renderContent : ( step : number , changeNext : () => void , changePrev : () => void ) => React.ReactNode , className? : string} |
|
||||||
) { |
|
||||||
|
|
||||||
const [currentStep, setCurrentStep] = React.useState(1); |
|
||||||
|
|
||||||
const handleNext = () => { |
|
||||||
if (currentStep < steps.length) { |
|
||||||
setCurrentStep((prev) => prev + 1); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
const handlePrev = () => { |
|
||||||
if (currentStep > 1) { |
|
||||||
setCurrentStep((prev) => prev - 1); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
const handleStepClick = (stepNumber: number) => { |
|
||||||
setCurrentStep(stepNumber); |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={`max-w-[70vw] w-full mx-auto ${className}`}> |
|
||||||
<div className=" space-y-6"> |
|
||||||
<Stepper |
|
||||||
steps={steps} |
|
||||||
currentStep={currentStep} |
|
||||||
onStepClick={handleStepClick} |
|
||||||
/> |
|
||||||
<div> |
|
||||||
{ |
|
||||||
renderContent(currentStep , handleNext , handlePrev) |
|
||||||
} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
@ -1,126 +0,0 @@ |
|||||||
import Calendar from "@sbmdkl/nepali-datepicker-reactjs"; |
|
||||||
import '@sbmdkl/nepali-datepicker-reactjs/dist/index.css'; |
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react"; |
|
||||||
import NepaliDate from "nepali-date-converter"; |
|
||||||
import { AdToBs, getTomorrowEnglishDate, getYesterdayEnglishDate } from "@/helpers/date.helper" |
|
||||||
import { Label } from "../ui/label"; |
|
||||||
|
|
||||||
interface NepaliDateFieldProps { |
|
||||||
children?: React.ReactNode; |
|
||||||
wrapperClassName?: string; |
|
||||||
id?: string; |
|
||||||
className?: string; |
|
||||||
label?: string; |
|
||||||
labelStyle?: "label-left" | 'label-top'; |
|
||||||
labelWidth?: String; |
|
||||||
name?: string; |
|
||||||
status?: string; |
|
||||||
disabled?: boolean; |
|
||||||
value?: any; |
|
||||||
defaultValue?: any; |
|
||||||
formGroup?: string; |
|
||||||
hasIcon?: string; |
|
||||||
size?: string; |
|
||||||
placeholder?: string; |
|
||||||
rows?: number; |
|
||||||
hint?: string; |
|
||||||
required?: boolean; |
|
||||||
hidden?: boolean; |
|
||||||
fieldErrors?: Array<string>; |
|
||||||
helpText?: String; |
|
||||||
onChange?: (e: any) => void; |
|
||||||
step?: string; |
|
||||||
disableFutureDate?: boolean; |
|
||||||
position?: "bottom" | "left"; |
|
||||||
disablePastDate?: boolean; |
|
||||||
isToday? : boolean |
|
||||||
} |
|
||||||
|
|
||||||
const NepaliDateField: React.FC<NepaliDateFieldProps> = ({ |
|
||||||
className = "", |
|
||||||
id, |
|
||||||
wrapperClassName = "", |
|
||||||
children, |
|
||||||
label, |
|
||||||
labelStyle, |
|
||||||
labelWidth, |
|
||||||
name, |
|
||||||
status, |
|
||||||
disabled, |
|
||||||
value, |
|
||||||
defaultValue, |
|
||||||
formGroup, |
|
||||||
hasIcon, |
|
||||||
size, |
|
||||||
placeholder, |
|
||||||
rows, |
|
||||||
hint, |
|
||||||
required, |
|
||||||
fieldErrors = [], |
|
||||||
helpText, |
|
||||||
hidden = false, |
|
||||||
onChange, |
|
||||||
disableFutureDate, |
|
||||||
position = "bottom", |
|
||||||
disablePastDate, |
|
||||||
isToday = false , |
|
||||||
...props |
|
||||||
}) => { |
|
||||||
const today = new NepaliDate().format("YYYY-MM-DD"); |
|
||||||
const tomorrow = getTomorrowEnglishDate(); |
|
||||||
const previousDay = getYesterdayEnglishDate(); |
|
||||||
const currentNepaliDate = new NepaliDate().format("YYYY-MM-DD"); |
|
||||||
const tomorrowNepaliDate = AdToBs(tomorrow); |
|
||||||
const yesterdayNepaliDate = AdToBs(previousDay); |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
if (disabled) { |
|
||||||
document.querySelector('._1oYOS')?.setAttribute('disabled', 'true'); |
|
||||||
} else { |
|
||||||
document.querySelector('._1oYOS')?.removeAttribute('disabled'); |
|
||||||
} |
|
||||||
}, [disabled]); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
className={`w-full flex-col ${wrapperClassName} ${ |
|
||||||
position === "left" ? "inputLeft" : "" |
|
||||||
}`}
|
|
||||||
> |
|
||||||
{label && ( |
|
||||||
<Label id={id && id}> |
|
||||||
{label} |
|
||||||
</Label> |
|
||||||
)} |
|
||||||
<div className={label && 'w-full mt-2'}> |
|
||||||
<Calendar |
|
||||||
onChange={onChange} |
|
||||||
hideDefaultValue={defaultValue ? false : (isToday ? false : true)} |
|
||||||
defaultDate={defaultValue ?? (isToday ? today : undefined)} |
|
||||||
placeholder={"YYYY-MM-DD"} |
|
||||||
language={"en"} |
|
||||||
value={value ?? ''} |
|
||||||
className={`border rounded-md py-1.5 px-1.5 w-48 ${className} text-xs `} |
|
||||||
maxDate={disableFutureDate ? tomorrowNepaliDate : undefined} |
|
||||||
minDate={disablePastDate ? yesterdayNepaliDate : undefined} |
|
||||||
disabled={disabled} |
|
||||||
/> |
|
||||||
{children} |
|
||||||
{helpText && ( |
|
||||||
<div className="text-gray-800 text-xs mt-1">{helpText}</div> |
|
||||||
)} |
|
||||||
<span className="text-error-500 text-xs"> |
|
||||||
{fieldErrors.map((item: string, index: any) => ( |
|
||||||
<span key={index}> |
|
||||||
{item} |
|
||||||
<br></br> |
|
||||||
</span> |
|
||||||
))} |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default NepaliDateField; |
|
@ -1,125 +0,0 @@ |
|||||||
import React, { useEffect, useRef } from "react"; |
|
||||||
import { |
|
||||||
Select, |
|
||||||
SelectContent, |
|
||||||
SelectItem, |
|
||||||
SelectTrigger, |
|
||||||
SelectValue, |
|
||||||
} from "@/components/ui/select"; |
|
||||||
import { Label } from "@/components/ui/label"; |
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { ChoiceType } from "@/helpers/commonSchema/common.schema"; |
|
||||||
|
|
||||||
interface SelectFieldProps { |
|
||||||
className?: string; |
|
||||||
id?: string; |
|
||||||
label?: string; |
|
||||||
labelStyle?: string; |
|
||||||
labelWidth?: string; |
|
||||||
name: string; |
|
||||||
labelClassName?: string; |
|
||||||
placeholder?: string; |
|
||||||
defaultValue?: string | number | boolean; |
|
||||||
value?: string | number | boolean; |
|
||||||
required?: boolean; |
|
||||||
wrapperClassName?: string; |
|
||||||
options: { value: string | number; label: string }[]; |
|
||||||
fieldErrors?: Array<string>; |
|
||||||
clearSelectValue?: boolean; |
|
||||||
setClearSelectValue?: React.Dispatch<React.SetStateAction<boolean>>; |
|
||||||
isDisabled?: boolean; |
|
||||||
onChange?: (value: ChoiceType | any) => void; |
|
||||||
} |
|
||||||
|
|
||||||
const SelectField: React.FC<SelectFieldProps> = ({ |
|
||||||
className = "", |
|
||||||
id, |
|
||||||
label, |
|
||||||
labelStyle, |
|
||||||
name, |
|
||||||
defaultValue, |
|
||||||
value, |
|
||||||
placeholder = `Select ${label}`, |
|
||||||
required, |
|
||||||
options, |
|
||||||
wrapperClassName = "", |
|
||||||
fieldErrors = [], |
|
||||||
clearSelectValue = false, |
|
||||||
setClearSelectValue, |
|
||||||
isDisabled, |
|
||||||
onChange, |
|
||||||
labelClassName |
|
||||||
}) => { |
|
||||||
const selectRef = useRef<HTMLButtonElement>(null); |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
if (clearSelectValue === true) { |
|
||||||
onChange?.(""); |
|
||||||
setClearSelectValue?.(false); |
|
||||||
} |
|
||||||
}, [clearSelectValue, setClearSelectValue, onChange]); |
|
||||||
|
|
||||||
const wrapperClasses = cn( |
|
||||||
wrapperClassName, |
|
||||||
labelStyle === "label-left" && "flex justify-between items-center gap-2" |
|
||||||
); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={wrapperClasses}> |
|
||||||
{label && ( |
|
||||||
<Label
|
|
||||||
htmlFor={id ?? name}
|
|
||||||
className={cn(labelClassName)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
{required && <span className="text-destructive ml-1">*</span>} |
|
||||||
</Label> |
|
||||||
)} |
|
||||||
|
|
||||||
<div className={cn( |
|
||||||
"w-full", |
|
||||||
labelStyle !== "label-left" && "mt-2" |
|
||||||
)}> |
|
||||||
<Select |
|
||||||
defaultValue={defaultValue?.toString()} |
|
||||||
value={value?.toString()} |
|
||||||
onValueChange={onChange} |
|
||||||
disabled={isDisabled} |
|
||||||
|
|
||||||
> |
|
||||||
<SelectTrigger
|
|
||||||
ref={selectRef} |
|
||||||
className={cn( |
|
||||||
"w-full font-normal", |
|
||||||
className |
|
||||||
)} |
|
||||||
id={id ?? name} |
|
||||||
> |
|
||||||
<SelectValue placeholder={placeholder} /> |
|
||||||
</SelectTrigger> |
|
||||||
<SelectContent className="max-h-[200px]"> |
|
||||||
{options.map((option) => ( |
|
||||||
<SelectItem
|
|
||||||
key={option.value}
|
|
||||||
value={option.value.toString()} |
|
||||||
> |
|
||||||
{option.label} |
|
||||||
</SelectItem> |
|
||||||
))} |
|
||||||
</SelectContent> |
|
||||||
</Select> |
|
||||||
|
|
||||||
{fieldErrors.map((error, index) => ( |
|
||||||
<span
|
|
||||||
key={index}
|
|
||||||
className="block text-destructive text-xs mt-1" |
|
||||||
> |
|
||||||
{error} |
|
||||||
</span> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default SelectField; |
|
@ -1,54 +0,0 @@ |
|||||||
import React from "react" |
|
||||||
import {
|
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLink,
|
|
||||||
BreadcrumbList,
|
|
||||||
BreadcrumbPage,
|
|
||||||
BreadcrumbSeparator
|
|
||||||
|
|
||||||
} from "@/components/ui/breadcrumb" |
|
||||||
import { Link } from "lucide-react" |
|
||||||
|
|
||||||
interface BreadCrumbItemInterface { |
|
||||||
title : string |
|
||||||
href : string |
|
||||||
|
|
||||||
} |
|
||||||
interface BreadCrumbNavPropsInterface { |
|
||||||
breadCrumbItems : BreadCrumbItemInterface[] |
|
||||||
} |
|
||||||
|
|
||||||
const BreadCrumbNav : React.FC<BreadCrumbNavPropsInterface> = ({ |
|
||||||
breadCrumbItems |
|
||||||
}) => { |
|
||||||
|
|
||||||
return( |
|
||||||
<Breadcrumb> |
|
||||||
<BreadcrumbList> |
|
||||||
{ |
|
||||||
breadCrumbItems?.length > 1 && |
|
||||||
breadCrumbItems.map((item: BreadCrumbItemInterface, index: number) => { |
|
||||||
const isLastItem = index === breadCrumbItems.length - 1; |
|
||||||
return ( |
|
||||||
<React.Fragment key={index}> |
|
||||||
<BreadcrumbItem> |
|
||||||
{isLastItem ? ( |
|
||||||
<BreadcrumbPage>{item.title}</BreadcrumbPage> |
|
||||||
) : ( |
|
||||||
<BreadcrumbLink href={item.href}> |
|
||||||
{item.title} |
|
||||||
</BreadcrumbLink> |
|
||||||
)} |
|
||||||
</BreadcrumbItem> |
|
||||||
{!isLastItem && <BreadcrumbSeparator />} |
|
||||||
</React.Fragment> |
|
||||||
); |
|
||||||
}) |
|
||||||
} |
|
||||||
</BreadcrumbList> |
|
||||||
</Breadcrumb> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default BreadCrumbNav |
|
@ -1,377 +0,0 @@ |
|||||||
import React, { useState, useCallback, useMemo, useEffect, useRef } from "react" |
|
||||||
import { useSearchParams, useRouter } from "next/navigation" |
|
||||||
import { ChevronDown, Cross, Loader2, MoreHorizontal, X } from "lucide-react" |
|
||||||
import { |
|
||||||
ColumnDef, |
|
||||||
ColumnFiltersState, |
|
||||||
SortingState, |
|
||||||
VisibilityState, |
|
||||||
flexRender, |
|
||||||
getCoreRowModel, |
|
||||||
getFilteredRowModel, |
|
||||||
getPaginationRowModel, |
|
||||||
getSortedRowModel, |
|
||||||
useReactTable, |
|
||||||
} from "@tanstack/react-table" |
|
||||||
import { Button } from "@/components/ui/button" |
|
||||||
import { Checkbox } from "@/components/ui/checkbox" |
|
||||||
import { |
|
||||||
DropdownMenu, |
|
||||||
DropdownMenuCheckboxItem, |
|
||||||
DropdownMenuContent, |
|
||||||
DropdownMenuItem, |
|
||||||
DropdownMenuTrigger, |
|
||||||
} from "@/components/ui/dropdown-menu" |
|
||||||
import { Input } from "@/components/ui/input" |
|
||||||
import { |
|
||||||
Table, |
|
||||||
TableBody, |
|
||||||
TableCell, |
|
||||||
TableHead, |
|
||||||
TableHeader, |
|
||||||
TableRow, |
|
||||||
} from "@/components/ui/table" |
|
||||||
import { DeleteInstances } from "../DeleteInstances/DeleteInstances" |
|
||||||
import BulkUpload from "../../elements/BulkUpload" |
|
||||||
import Download from "../../elements/Download" |
|
||||||
import TableSkeleton from "../../elements/TableSkeleton" |
|
||||||
|
|
||||||
const useDebounce = (callback: Function, delay: number) => { |
|
||||||
const timeoutRef = React.useRef<NodeJS.Timeout>(); |
|
||||||
|
|
||||||
React.useEffect(() => { |
|
||||||
return () => { |
|
||||||
if (timeoutRef.current) { |
|
||||||
clearTimeout(timeoutRef.current); |
|
||||||
} |
|
||||||
}; |
|
||||||
}, []); |
|
||||||
|
|
||||||
return useCallback((...args: any[]) => { |
|
||||||
if (timeoutRef.current) { |
|
||||||
clearTimeout(timeoutRef.current); |
|
||||||
} |
|
||||||
|
|
||||||
timeoutRef.current = setTimeout(() => { |
|
||||||
callback(...args); |
|
||||||
}, delay); |
|
||||||
}, [callback, delay]); |
|
||||||
}; |
|
||||||
|
|
||||||
interface DataTableProps<TData> { |
|
||||||
data: TData[] |
|
||||||
columns: ColumnDef<TData>[] |
|
||||||
searchKey?: string |
|
||||||
showCheckbox?: boolean |
|
||||||
actionDropdown?: any |
|
||||||
sn?: number |
|
||||||
customFilter?: React.ReactNode |
|
||||||
mutate: () => void |
|
||||||
isLoading?: boolean |
|
||||||
uploadURL?: string |
|
||||||
deleteURL?: string |
|
||||||
downloadSampleURL? : string |
|
||||||
downloadBtnLable? : string |
|
||||||
} |
|
||||||
|
|
||||||
export function DataTable<TData>({ |
|
||||||
data = [], |
|
||||||
columns: userColumns, |
|
||||||
searchKey, |
|
||||||
showCheckbox = false, |
|
||||||
actionDropdown, |
|
||||||
customFilter, |
|
||||||
mutate, |
|
||||||
uploadURL, |
|
||||||
isLoading = false, |
|
||||||
deleteURL, |
|
||||||
downloadSampleURL, |
|
||||||
downloadBtnLable |
|
||||||
}: DataTableProps<TData>) { |
|
||||||
const searchParams = useSearchParams() |
|
||||||
const router = useRouter() |
|
||||||
const [isSearching, setIsSearching] = useState(false) |
|
||||||
const inputRef = useRef<HTMLInputElement>(null) |
|
||||||
const shouldFocusRef = useRef(false) |
|
||||||
|
|
||||||
// Initialize states with proper types
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]) |
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]) |
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({}) |
|
||||||
const [rowSelection, setRowSelection] = useState({}) |
|
||||||
const [deleteIds, setDeleteIds] = useState([]) |
|
||||||
|
|
||||||
// Initialize filter state from URL
|
|
||||||
const [localFilter, setLocalFilter] = useState(searchParams.get(searchKey ?? 'name') || "") |
|
||||||
|
|
||||||
// Memoize the columns configuration
|
|
||||||
const finalColumns = useMemo(() => { |
|
||||||
let cols: ColumnDef<TData>[] = [] |
|
||||||
|
|
||||||
if (showCheckbox) { |
|
||||||
cols.push({ |
|
||||||
id: "select", |
|
||||||
header: ({ table }) => ( |
|
||||||
<Checkbox |
|
||||||
checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")} |
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} |
|
||||||
aria-label="Select all" |
|
||||||
disabled={isLoading} |
|
||||||
/> |
|
||||||
), |
|
||||||
cell: ({ row }) => ( |
|
||||||
<Checkbox |
|
||||||
checked={row.getIsSelected()} |
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)} |
|
||||||
aria-label="Select row" |
|
||||||
disabled={isLoading} |
|
||||||
/> |
|
||||||
), |
|
||||||
enableSorting: false, |
|
||||||
enableHiding: false, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
cols = [...cols, ...userColumns] |
|
||||||
|
|
||||||
if (actionDropdown) { |
|
||||||
cols.push({ |
|
||||||
...actionDropdown, |
|
||||||
header: "Actions", |
|
||||||
enableSorting: false, |
|
||||||
enableHiding: false, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
return cols |
|
||||||
}, [userColumns, showCheckbox, actionDropdown, isLoading]) |
|
||||||
|
|
||||||
const tableData = useMemo(() => { |
|
||||||
if (!data) return []; |
|
||||||
return data.map((item: any) => ({ |
|
||||||
...item, |
|
||||||
})); |
|
||||||
}, [data]); |
|
||||||
|
|
||||||
// Create table instance
|
|
||||||
const table = useReactTable({ |
|
||||||
data: tableData, |
|
||||||
columns: finalColumns, |
|
||||||
onSortingChange: setSorting, |
|
||||||
onColumnFiltersChange: setColumnFilters, |
|
||||||
getCoreRowModel: getCoreRowModel(), |
|
||||||
getPaginationRowModel: getPaginationRowModel(), |
|
||||||
getSortedRowModel: getSortedRowModel(), |
|
||||||
getFilteredRowModel: getFilteredRowModel(), |
|
||||||
onColumnVisibilityChange: setColumnVisibility, |
|
||||||
onRowSelectionChange: setRowSelection, |
|
||||||
state: { |
|
||||||
sorting, |
|
||||||
columnFilters, |
|
||||||
columnVisibility, |
|
||||||
rowSelection, |
|
||||||
}, |
|
||||||
enableFilters: true, |
|
||||||
manualFiltering: false, |
|
||||||
}) |
|
||||||
|
|
||||||
// Debounced URL update
|
|
||||||
const debouncedUpdateURL = useCallback( |
|
||||||
useDebounce(async (value: string) => { |
|
||||||
try { |
|
||||||
const currentParams = new URLSearchParams(searchParams?.toString() || ''); |
|
||||||
currentParams.set(searchKey ?? 'name', value); |
|
||||||
router.replace(`?${currentParams.toString()}`); |
|
||||||
if (typeof mutate === 'function') { |
|
||||||
await mutate(); |
|
||||||
} |
|
||||||
} catch (error) { |
|
||||||
console.error('Error updating search:', error); |
|
||||||
} finally { |
|
||||||
setIsSearching(false); |
|
||||||
shouldFocusRef.current = true; |
|
||||||
} |
|
||||||
}, 500), |
|
||||||
[searchParams, router, mutate, searchKey] |
|
||||||
); |
|
||||||
|
|
||||||
// Clear search parameters
|
|
||||||
const clearSearchParams = () => { |
|
||||||
const column = table.getColumn(searchKey as string); |
|
||||||
if (column) { |
|
||||||
column.setFilterValue(""); |
|
||||||
} |
|
||||||
setLocalFilter(""); |
|
||||||
setIsSearching(false); |
|
||||||
router.replace(window.location.pathname); |
|
||||||
if (typeof mutate === 'function') { |
|
||||||
mutate(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Handle input filter change
|
|
||||||
const handleFilterChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => { |
|
||||||
const value = event.target.value; |
|
||||||
setLocalFilter(value); |
|
||||||
setIsSearching(true); |
|
||||||
shouldFocusRef.current = true; |
|
||||||
debouncedUpdateURL(value); |
|
||||||
}, [debouncedUpdateURL]); |
|
||||||
|
|
||||||
// Handle focus after loading/searching states change
|
|
||||||
useEffect(() => { |
|
||||||
if (!isLoading && !isSearching && shouldFocusRef.current && inputRef.current) { |
|
||||||
inputRef.current.focus(); |
|
||||||
shouldFocusRef.current = false; |
|
||||||
} |
|
||||||
}, [isLoading, isSearching]); |
|
||||||
|
|
||||||
// Initialize filter from URL
|
|
||||||
useEffect(() => { |
|
||||||
const urlFilter = searchParams?.get(searchKey ?? 'name'); |
|
||||||
if (searchKey && urlFilter) { |
|
||||||
setLocalFilter(urlFilter); |
|
||||||
} |
|
||||||
}, [searchParams, searchKey]); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className="w-full"> |
|
||||||
<div className="flex items-center py-4 justify-between"> |
|
||||||
<div className="flex gap-2"> |
|
||||||
{searchKey && ( |
|
||||||
<div className="relative"> |
|
||||||
<Input |
|
||||||
placeholder={`Filter ${searchKey}...`} |
|
||||||
value={localFilter} |
|
||||||
onChange={handleFilterChange} |
|
||||||
className="max-w-sm" |
|
||||||
disabled={isLoading} |
|
||||||
ref={inputRef} |
|
||||||
/> |
|
||||||
{(isLoading || isSearching) ? ( |
|
||||||
<div className="absolute right-2 top-1/2 -translate-y-1/2"> |
|
||||||
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> |
|
||||||
</div> |
|
||||||
) : searchParams?.get(searchKey) ? ( |
|
||||||
<button
|
|
||||||
className="absolute right-2 top-1/2 -translate-y-1/2" |
|
||||||
onClick={clearSearchParams} |
|
||||||
> |
|
||||||
<X className="h-4 w-4 text-muted-foreground" /> |
|
||||||
</button> |
|
||||||
) : null} |
|
||||||
</div> |
|
||||||
)} |
|
||||||
{customFilter} |
|
||||||
</div> |
|
||||||
<div className="w-fit flex gap-4"> |
|
||||||
{table.getSelectedRowModel().rows.map(row => row.original?.id)?.length > 0 && deleteURL &&
|
|
||||||
<DeleteInstances
|
|
||||||
url={deleteURL} |
|
||||||
mutate={mutate} |
|
||||||
ids={table.getSelectedRowModel().rows.map(row => row.original?.id)} |
|
||||||
variant={'outline'} |
|
||||||
/> |
|
||||||
} |
|
||||||
<DropdownMenu> |
|
||||||
<DropdownMenuTrigger asChild> |
|
||||||
<Button variant="outline" className="ml-auto" disabled={isLoading}> |
|
||||||
Columns <ChevronDown className="ml-2 h-4 w-4" /> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
<DropdownMenuContent align="end"> |
|
||||||
{table |
|
||||||
.getAllColumns() |
|
||||||
.filter((column) => column.getCanHide()) |
|
||||||
.map((column) => ( |
|
||||||
<DropdownMenuCheckboxItem |
|
||||||
key={column.id} |
|
||||||
className="capitalize" |
|
||||||
checked={column.getIsVisible()} |
|
||||||
onCheckedChange={(value) => column.toggleVisibility(!!value)} |
|
||||||
> |
|
||||||
{column.id} |
|
||||||
</DropdownMenuCheckboxItem> |
|
||||||
))} |
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
{(uploadURL || downloadSampleURL) &&
|
|
||||||
<DropdownMenu> |
|
||||||
<DropdownMenuTrigger asChild className=""> |
|
||||||
<Button variant="ghost" className="h-8 w-8 p-0"> |
|
||||||
<span className="sr-only">Open menu</span> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
<DropdownMenuContent align="end"> |
|
||||||
{uploadURL &&
|
|
||||||
<DropdownMenuItem asChild> |
|
||||||
<BulkUpload submitUrl={uploadURL} mutate={mutate}/> |
|
||||||
</DropdownMenuItem> |
|
||||||
} |
|
||||||
{ |
|
||||||
downloadSampleURL &&
|
|
||||||
<DropdownMenuItem asChild className="mt-2"> |
|
||||||
<Download url={downloadSampleURL} label={downloadBtnLable}/> |
|
||||||
</DropdownMenuItem> |
|
||||||
} |
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="rounded-md border"> |
|
||||||
<Table className="custom-scrollbar" key={data?.length}> |
|
||||||
<TableHeader> |
|
||||||
{table.getHeaderGroups().map((headerGroup) => ( |
|
||||||
<TableRow key={headerGroup.id}> |
|
||||||
{headerGroup.headers.map((header) => ( |
|
||||||
<TableHead key={header.id}> |
|
||||||
{header.isPlaceholder |
|
||||||
? null |
|
||||||
: flexRender( |
|
||||||
header.column.columnDef.header, |
|
||||||
header.getContext() |
|
||||||
)} |
|
||||||
</TableHead> |
|
||||||
))} |
|
||||||
</TableRow> |
|
||||||
))} |
|
||||||
</TableHeader> |
|
||||||
<TableBody> |
|
||||||
{isLoading ? ( |
|
||||||
<TableSkeleton columns={finalColumns.length} rows={5}/> |
|
||||||
) : table.getRowModel().rows.length > 0 ? ( |
|
||||||
table.getRowModel().rows.map((row) => ( |
|
||||||
<TableRow |
|
||||||
key={row.id} |
|
||||||
data-state={row.getIsSelected() && "selected"} |
|
||||||
> |
|
||||||
{row.getVisibleCells().map((cell) => ( |
|
||||||
<TableCell key={cell.id}> |
|
||||||
{flexRender( |
|
||||||
cell.column.columnDef.cell, |
|
||||||
cell.getContext() |
|
||||||
)} |
|
||||||
</TableCell> |
|
||||||
))} |
|
||||||
</TableRow> |
|
||||||
)) |
|
||||||
) : ( |
|
||||||
<TableRow> |
|
||||||
<TableCell |
|
||||||
colSpan={finalColumns.length} |
|
||||||
className="h-24 text-center" |
|
||||||
> |
|
||||||
No results. |
|
||||||
</TableCell> |
|
||||||
</TableRow> |
|
||||||
)} |
|
||||||
</TableBody> |
|
||||||
</Table> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default DataTable; |
|
@ -1,117 +0,0 @@ |
|||||||
import { |
|
||||||
AlertDialog, |
|
||||||
AlertDialogAction, |
|
||||||
AlertDialogCancel, |
|
||||||
AlertDialogContent, |
|
||||||
AlertDialogDescription, |
|
||||||
AlertDialogFooter, |
|
||||||
AlertDialogHeader, |
|
||||||
AlertDialogTitle, |
|
||||||
AlertDialogTrigger, |
|
||||||
} from "@/components/ui/alert-dialog" |
|
||||||
import { Button } from "@/components/ui/button" |
|
||||||
import { fetchHeader } from "@/helpers/fetch.helper"; |
|
||||||
import { useToast } from "@/hooks/use-toast"; |
|
||||||
import { Loader, Trash2 } from "lucide-react"; |
|
||||||
import { useState } from "react" |
|
||||||
|
|
||||||
export function DeleteInstances({ |
|
||||||
url, |
|
||||||
mutate, |
|
||||||
ids, |
|
||||||
variant = "ghost" , |
|
||||||
// open,
|
|
||||||
// onOpenChange,
|
|
||||||
showTitle = true , |
|
||||||
className |
|
||||||
}: { |
|
||||||
url: string; |
|
||||||
mutate: () => void; |
|
||||||
ids: Array<string>; |
|
||||||
variant? : "ghost" | "link" | "default" | "destructive" | "outline" | "secondary" | null | undefined |
|
||||||
className? : string |
|
||||||
showTitle? : boolean |
|
||||||
// open: boolean;
|
|
||||||
// onOpenChange: (value: boolean) => void;
|
|
||||||
}) { |
|
||||||
const [showModal , setShowModal] = useState<boolean>(false)
|
|
||||||
const [loading, setLoading] = useState<boolean>(false); |
|
||||||
const { toast } = useToast(); |
|
||||||
|
|
||||||
const handleDelete = async (e: React.MouseEvent): Promise<void> => { |
|
||||||
|
|
||||||
const formData = new FormData(); |
|
||||||
ids.forEach((id : string , index : number) => { |
|
||||||
formData.append(`ids[${index}]` , id) |
|
||||||
}) |
|
||||||
|
|
||||||
try { |
|
||||||
setLoading(true); |
|
||||||
const res = await fetch(url, { |
|
||||||
headers: fetchHeader(), |
|
||||||
method: "POST", |
|
||||||
body: formData, |
|
||||||
}); |
|
||||||
const data = await res.json(); |
|
||||||
if (data?.success) { |
|
||||||
toast({ |
|
||||||
title: "Success", |
|
||||||
description: data?.message, |
|
||||||
variant: "success", |
|
||||||
}); |
|
||||||
mutate(); |
|
||||||
setShowModal(false); // Close the dialog on success
|
|
||||||
}else{ |
|
||||||
toast({ |
|
||||||
variant: "destructive", |
|
||||||
title: "Error", |
|
||||||
description: data?.message, |
|
||||||
}); |
|
||||||
} |
|
||||||
} catch (error: any) { |
|
||||||
toast({ |
|
||||||
variant: "destructive", |
|
||||||
title: "Error", |
|
||||||
description: error?.message, |
|
||||||
}); |
|
||||||
} finally { |
|
||||||
setLoading(false); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<AlertDialog open={showModal} onOpenChange={setShowModal}> |
|
||||||
<AlertDialogTrigger asChild> |
|
||||||
<Button |
|
||||||
variant={variant} |
|
||||||
size="sm" |
|
||||||
className={`text-red-500 hover:text-red-700 !w-full text-left flex !px-2 h-auto min-h-[36px]
|
|
||||||
${variant == 'outline' ? 'justify-center' : 'justify-start'} ${className}`}
|
|
||||||
> |
|
||||||
<Trash2 className="h-4 w-4" /> |
|
||||||
{showTitle && 'Delete'} |
|
||||||
{loading ? <Loader /> : null} |
|
||||||
</Button> |
|
||||||
</AlertDialogTrigger> |
|
||||||
<AlertDialogContent> |
|
||||||
<AlertDialogHeader> |
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> |
|
||||||
<AlertDialogDescription> |
|
||||||
This action cannot be undone. This will delete your data. |
|
||||||
</AlertDialogDescription> |
|
||||||
</AlertDialogHeader> |
|
||||||
<AlertDialogFooter> |
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel> |
|
||||||
<AlertDialogAction |
|
||||||
className="bg-red-500 hover:bg-red-300" |
|
||||||
onClick={(e) => handleDelete(e)} |
|
||||||
> |
|
||||||
Continue |
|
||||||
</AlertDialogAction> |
|
||||||
</AlertDialogFooter> |
|
||||||
</AlertDialogContent> |
|
||||||
</AlertDialog> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@ |
|||||||
import Link from "next/link"; |
|
||||||
|
|
||||||
export default function AppFooter() { |
|
||||||
return ( |
|
||||||
<div className="z-20 w-full bg-background/95 shadow backdrop-blur supports-[backdrop-filter]:bg-background/60"> |
|
||||||
<div className="mx-4 md:mx-8 flex h-14 items-center"> |
|
||||||
<p className="text-xs md:text-sm leading-loose text-muted-foreground text-left"> |
|
||||||
©copyright{" "} |
|
||||||
<Link |
|
||||||
href="www.kumarijob.com" |
|
||||||
target="_blank" |
|
||||||
rel="noopener noreferrer" |
|
||||||
className="font-medium underline underline-offset-4" |
|
||||||
> |
|
||||||
Kumari Job |
|
||||||
</Link> |
|
||||||
. All rights reserved |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
import { SearchIcon } from "lucide-react"; |
|
||||||
import { ModeToggle } from "./_partials/modeToggle"; |
|
||||||
import { SheetMenu } from "./_partials/sheetMenu"; |
|
||||||
import { UserNav } from "./_partials/userNav"; |
|
||||||
import UserNotifications from "./_partials/userNotifications"; |
|
||||||
import { Input } from "@/components/ui/input"; |
|
||||||
|
|
||||||
|
|
||||||
const AppHeader : React.FC = () => { |
|
||||||
return ( |
|
||||||
<header className="sticky top-0 z-10 w-full bg-background/95 shadow backdrop-blur
|
|
||||||
supports-[backdrop-filter]:bg-background/60
|
|
||||||
dark:shadow-secondary"> |
|
||||||
<div className="flex h-14 items-center container py-4 px-4 sm:px-8 mx-auto"> |
|
||||||
<div className="flex items-center space-x-4 lg:space-x-0 mr-4"> |
|
||||||
<SheetMenu /> |
|
||||||
</div> |
|
||||||
<div className="relative flex-1 max-w-md md:max-w-lg sm:ml-0 ml-2"> |
|
||||||
<SearchIcon className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" /> |
|
||||||
<Input |
|
||||||
type="search" |
|
||||||
placeholder="Search..." |
|
||||||
className="w-full rounded-lg bg-muted pl-8 pr-4 py-2 text-sm focus:outline-none focus:ring-1
|
|
||||||
focus:ring-primary focus:border-primary" |
|
||||||
/> |
|
||||||
</div> |
|
||||||
<div className="flex flex-1 items-center gap-3 justify-end"> |
|
||||||
<UserNotifications /> |
|
||||||
<ModeToggle /> |
|
||||||
<UserNav /> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</header> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default AppHeader |
|
||||||
|
|
||||||
|
|
@ -1,66 +0,0 @@ |
|||||||
import React, { useContext } from 'react'; |
|
||||||
import { |
|
||||||
DropdownMenu, |
|
||||||
DropdownMenuContent, |
|
||||||
DropdownMenuItem, |
|
||||||
DropdownMenuTrigger, |
|
||||||
} from "@/components/ui/dropdown-menu"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { GraduationCap, Grid3x3, Headset, UserRoundCog, UserSearch, Warehouse } from "lucide-react"; |
|
||||||
import Link from 'next/link'; |
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; |
|
||||||
import { AuthContext } from '@/helpers/context/AuthProvider'; |
|
||||||
|
|
||||||
const apps = [ |
|
||||||
{ icon: UserRoundCog, name: 'PIMS', color: 'text-red-600' , link : `${process.env.NEXT_PUBLIC_HRMS_FRONTEND}`}, |
|
||||||
{ icon: Warehouse, name: 'Asset', color: 'text-orange-600' , link : `${process.env.NEXT_PUBLIC_HRMS_FRONTEND}`}, |
|
||||||
{ icon: Headset, name: 'CRMS', color: 'text-blue-500' , link : `${process.env.NEXT_PUBLIC_CRM_FRONTEND}`}, |
|
||||||
{ icon: GraduationCap, name: 'TMS', color: 'text-yellow-500' , link : `${process.env.NEXT_PUBLIC_TMS_FRONTEND}`}, |
|
||||||
{ icon: UserSearch, name: 'Job Board', color: 'text-green-500' , link : `${process.env.NEXT_PUBLIC_EMPLOYEE_FRONTEND}`}, |
|
||||||
]; |
|
||||||
|
|
||||||
const ApplicationSwitch : React.FC = () => { |
|
||||||
|
|
||||||
const { user } = useContext(AuthContext)
|
|
||||||
return ( |
|
||||||
<DropdownMenu> |
|
||||||
<DropdownMenuTrigger asChild> |
|
||||||
<Button |
|
||||||
variant="outline" |
|
||||||
className="relative h-8 w-8 rounded-full" |
|
||||||
> |
|
||||||
<Grid3x3 className="w-[1.2rem] h-[1.2rem]"/> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
<DropdownMenuContent align="end" className="w-60 p-2"> |
|
||||||
<div className="grid grid-cols-3 gap-4 p-2"> |
|
||||||
<DropdownMenuItem> |
|
||||||
<Link href={`${process.env.IDP_FRONTEND}`} className=''> |
|
||||||
<Avatar className="h-8 w-8"> |
|
||||||
{ |
|
||||||
user?.pfpName ?
|
|
||||||
<AvatarImage src={user?.pfpName} alt={user.firstName} /> :
|
|
||||||
<AvatarFallback className="bg-transparent">uk</AvatarFallback> |
|
||||||
} |
|
||||||
</Avatar> |
|
||||||
<span>Profile</span> |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
{apps.map((app) => ( |
|
||||||
<DropdownMenuItem |
|
||||||
key={app.name} |
|
||||||
className="flex flex-col items-center justify-center p-2 focus:bg-accent cursor-pointer" |
|
||||||
> |
|
||||||
<Link href={app.link} className={`hover:text-${app.color}`}> |
|
||||||
<app.icon className={`h-6 w-6 mb-1 ${app.color}`} /> |
|
||||||
<span className="text-xs">{app.name}</span> |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default ApplicationSwitch; |
|
@ -1,40 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import * as React from "react"; |
|
||||||
// import { useTheme } from "next-themes";
|
|
||||||
import { MoonIcon, SunIcon } from "@radix-ui/react-icons"; |
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { |
|
||||||
Tooltip, |
|
||||||
TooltipContent, |
|
||||||
TooltipTrigger, |
|
||||||
TooltipProvider |
|
||||||
} from "@/components/ui/tooltip"; |
|
||||||
import { useTheme } from "@/hooks/use-theme"; |
|
||||||
|
|
||||||
export function ModeToggle() { |
|
||||||
|
|
||||||
const { toggleTheme, theme } = useTheme(); |
|
||||||
|
|
||||||
|
|
||||||
return ( |
|
||||||
<TooltipProvider disableHoverableContent> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger asChild> |
|
||||||
<Button |
|
||||||
className="rounded-full w-8 h-8 bg-background" |
|
||||||
variant="outline" |
|
||||||
size="icon" |
|
||||||
onClick={() => toggleTheme()} |
|
||||||
> |
|
||||||
<SunIcon className="w-[1.2rem] h-[1.2rem] rotate-90 scale-0 transition-transform ease-in-out duration-500 dark:rotate-0 dark:scale-100" /> |
|
||||||
<MoonIcon className="absolute w-[1.2rem] h-[1.2rem] rotate-0 scale-1000 transition-transform ease-in-out duration-500 dark:-rotate-90 dark:scale-0" /> |
|
||||||
<span className="sr-only">Switch Theme</span> |
|
||||||
</Button> |
|
||||||
</TooltipTrigger> |
|
||||||
<TooltipContent side="bottom">Switch Theme</TooltipContent> |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
); |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
import Link from "next/link"; |
|
||||||
import { MenuIcon, PanelsTopLeft } from "lucide-react"; |
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { |
|
||||||
Sheet, |
|
||||||
SheetHeader, |
|
||||||
SheetContent, |
|
||||||
SheetTrigger, |
|
||||||
SheetTitle |
|
||||||
} from "@/components/ui/sheet"; |
|
||||||
import { Menu } from "../../Sidebar/_partials/menu"; |
|
||||||
|
|
||||||
export function SheetMenu() { |
|
||||||
return ( |
|
||||||
<Sheet> |
|
||||||
<SheetTrigger className="lg:hidden" asChild> |
|
||||||
<Button className="h-8" variant="outline" size="icon"> |
|
||||||
<MenuIcon size={20} /> |
|
||||||
</Button> |
|
||||||
</SheetTrigger> |
|
||||||
<SheetContent className="sm:w-72 px-3 h-full flex flex-col" side="left"> |
|
||||||
<SheetHeader> |
|
||||||
<Button |
|
||||||
className="flex justify-center items-center pb-2 pt-1" |
|
||||||
variant="link" |
|
||||||
asChild |
|
||||||
> |
|
||||||
<Link href="/dashboard" className="flex items-center gap-2"> |
|
||||||
<PanelsTopLeft className="w-6 h-6 mr-1" /> |
|
||||||
<SheetTitle className="font-bold text-lg">Brand</SheetTitle> |
|
||||||
</Link> |
|
||||||
</Button> |
|
||||||
</SheetHeader> |
|
||||||
<Menu isOpen /> |
|
||||||
</SheetContent> |
|
||||||
</Sheet> |
|
||||||
); |
|
||||||
} |
|
@ -1,93 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import Link from "next/link"; |
|
||||||
import { LayoutGrid, LogOut, RefreshCcw, User } from "lucide-react"; |
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; |
|
||||||
|
|
||||||
import { |
|
||||||
Tooltip, |
|
||||||
TooltipContent, |
|
||||||
TooltipTrigger, |
|
||||||
TooltipProvider |
|
||||||
} from "@/components/ui/tooltip"; |
|
||||||
import { |
|
||||||
DropdownMenu, |
|
||||||
DropdownMenuContent, |
|
||||||
DropdownMenuGroup, |
|
||||||
DropdownMenuItem, |
|
||||||
DropdownMenuLabel, |
|
||||||
DropdownMenuSeparator, |
|
||||||
DropdownMenuTrigger |
|
||||||
} from "@/components/ui/dropdown-menu"; |
|
||||||
import { useContext } from "react"; |
|
||||||
import { AuthContext } from "@/helpers/context/AuthProvider"; |
|
||||||
|
|
||||||
export function UserNav() { |
|
||||||
|
|
||||||
const { user , logout } = useContext(AuthContext) |
|
||||||
|
|
||||||
return ( |
|
||||||
<DropdownMenu> |
|
||||||
<TooltipProvider disableHoverableContent> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger asChild> |
|
||||||
<DropdownMenuTrigger asChild> |
|
||||||
<Button |
|
||||||
variant="outline" |
|
||||||
className="relative h-8 w-8 rounded-full" |
|
||||||
> |
|
||||||
<Avatar className="h-8 w-8"> |
|
||||||
{ |
|
||||||
user?.pfpName ?
|
|
||||||
<AvatarImage src={user?.pfpName} alt={user.firstName} /> :
|
|
||||||
<AvatarFallback className="bg-transparent">uk</AvatarFallback> |
|
||||||
} |
|
||||||
</Avatar> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
</TooltipTrigger> |
|
||||||
<TooltipContent side="bottom">Profile</TooltipContent> |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
|
|
||||||
<DropdownMenuContent className="w-56" align="end" forceMount> |
|
||||||
<DropdownMenuLabel className="font-normal"> |
|
||||||
<div className="flex flex-col space-y-1"> |
|
||||||
<p className="text-sm font-medium leading-none">{user?.firstName}</p> |
|
||||||
<p className="text-xs leading-none text-muted-foreground"> |
|
||||||
{user?.email} |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
</DropdownMenuLabel> |
|
||||||
<DropdownMenuSeparator /> |
|
||||||
<DropdownMenuGroup> |
|
||||||
<DropdownMenuItem className="hover:cursor-pointer" asChild> |
|
||||||
<Link href="/dashboard" className="flex items-center"> |
|
||||||
<LayoutGrid className="w-4 h-4 mr-3 text-muted-foreground" /> |
|
||||||
Dashboard |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
<DropdownMenuItem className="hover:cursor-pointer" asChild> |
|
||||||
<Link href="/account" className="flex items-center"> |
|
||||||
<User className="w-4 h-4 mr-3 text-muted-foreground" /> |
|
||||||
Account |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
<DropdownMenuItem className="hover:cursor-pointer" asChild> |
|
||||||
<Link href="/account" className="flex items-center"> |
|
||||||
<RefreshCcw className="w-4 h-4 mr-3 text-muted-foreground" /> |
|
||||||
Switch Company |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
</DropdownMenuGroup> |
|
||||||
<DropdownMenuSeparator /> |
|
||||||
<DropdownMenuItem className="hover:cursor-pointer" onClick={() => logout()}> |
|
||||||
<LogOut className="w-4 h-4 mr-3 text-muted-foreground" /> |
|
||||||
Sign out |
|
||||||
</DropdownMenuItem> |
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
); |
|
||||||
} |
|
@ -1,123 +0,0 @@ |
|||||||
import { Button } from "@/components/ui/button" |
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger
|
|
||||||
} from "@/components/ui/dropdown-menu" |
|
||||||
import { BellIcon } from "lucide-react" |
|
||||||
import Image from "next/image" |
|
||||||
import Link from "next/link" |
|
||||||
|
|
||||||
const UserNotifications : React.FC = () => { |
|
||||||
return( |
|
||||||
<DropdownMenu> |
|
||||||
<DropdownMenuTrigger asChild> |
|
||||||
<Button |
|
||||||
variant="outline" |
|
||||||
className="relative h-8 w-8 rounded-full" |
|
||||||
> |
|
||||||
<BellIcon className="w-[1.2rem] h-[1.2rem]"/> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
|
|
||||||
<DropdownMenuContent className="w-80" align="end" forceMount> |
|
||||||
<DropdownMenuLabel className="font-normal"> |
|
||||||
<div className="flex flex-col space-y-1"> |
|
||||||
<p className="text-sm font-medium leading-none">Notifications</p> |
|
||||||
</div> |
|
||||||
</DropdownMenuLabel> |
|
||||||
<DropdownMenuSeparator /> |
|
||||||
<DropdownMenuGroup> |
|
||||||
<DropdownMenuItem className="hover:cursor-pointer" asChild> |
|
||||||
<p>Notification</p> |
|
||||||
</DropdownMenuItem> |
|
||||||
<DropdownMenuItem> |
|
||||||
<Link href="https://smarthr.dreamstechnologies.com/laravel/template/public/activity"> |
|
||||||
<div className="flex gap-2"> |
|
||||||
<div className="w-[35px] h-[35px] relative"> |
|
||||||
<Image src="https://smarthr.dreamstechnologies.com/laravel/template/public/build/img/profiles/avatar-27.jpg"
|
|
||||||
alt="Profile" fill className="aspect-square rounded-full"/> |
|
||||||
</div> |
|
||||||
<div className="flex-1"> |
|
||||||
<p className="mb-1"><span className="text-dark font-semibold">Shawn </span> |
|
||||||
performance in Math is below the threshold. |
|
||||||
</p> |
|
||||||
<span className="text-xs text-right block">Just Now</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
|
|
||||||
</DropdownMenuGroup> |
|
||||||
|
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
{/* <div class="d-flex flex-column"> |
|
||||||
<div class="border-bottom mb-3 pb-3"> |
|
||||||
<a href="https://smarthr.dreamstechnologies.com/laravel/template/public/activity"> |
|
||||||
<div class="d-flex"> |
|
||||||
<span class="avatar avatar-lg me-2 flex-shrink-0"> |
|
||||||
<img src="https://smarthr.dreamstechnologies.com/laravel/template/public/build/img/profiles/avatar-27.jpg" alt="Profile"> |
|
||||||
</span> |
|
||||||
<div class="flex-grow-1"> |
|
||||||
<p class="mb-1"><span class="text-dark fw-semibold">Shawn</span> |
|
||||||
performance in Math is below the threshold.</p> |
|
||||||
<span>Just Now</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
<div class="border-bottom mb-3 pb-3"> |
|
||||||
<a href="https://smarthr.dreamstechnologies.com/laravel/template/public/activity" class="pb-0"> |
|
||||||
<div class="d-flex"> |
|
||||||
<span class="avatar avatar-lg me-2 flex-shrink-0"> |
|
||||||
<img src="https://smarthr.dreamstechnologies.com/laravel/template/public/build/img/profiles/avatar-23.jpg" alt="Profile"> |
|
||||||
</span> |
|
||||||
<div class="flex-grow-1"> |
|
||||||
<p class="mb-1"><span class="text-dark fw-semibold">Sylvia</span> added |
|
||||||
appointment on 02:00 PM</p> |
|
||||||
<span>10 mins ago</span> |
|
||||||
<div class="d-flex justify-content-start align-items-center mt-1"> |
|
||||||
<span class="btn btn-light btn-sm me-2">Deny</span> |
|
||||||
<span class="btn btn-primary btn-sm">Approve</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
<div class="border-bottom mb-3 pb-3"> |
|
||||||
<a href="https://smarthr.dreamstechnologies.com/laravel/template/public/activity"> |
|
||||||
<div class="d-flex"> |
|
||||||
<span class="avatar avatar-lg me-2 flex-shrink-0"> |
|
||||||
<img src="https://smarthr.dreamstechnologies.com/laravel/template/public/build/img/profiles/avatar-25.jpg" alt="Profile"> |
|
||||||
</span> |
|
||||||
<div class="flex-grow-1"> |
|
||||||
<p class="mb-1">New student record <span class="text-dark fw-semibold"> George</span> is created by <span class="text-dark fw-semibold">Teressa</span></p> |
|
||||||
<span>2 hrs ago</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
<div class="border-0 mb-3 pb-0"> |
|
||||||
<a href="https://smarthr.dreamstechnologies.com/laravel/template/public/activity"> |
|
||||||
<div class="d-flex"> |
|
||||||
<span class="avatar avatar-lg me-2 flex-shrink-0"> |
|
||||||
<img src="https://smarthr.dreamstechnologies.com/laravel/template/public/build/img/profiles/avatar-01.jpg" alt="Profile"> |
|
||||||
</span> |
|
||||||
<div class="flex-grow-1"> |
|
||||||
<p class="mb-1">A new teacher record for <span class="text-dark fw-semibold">Elisa</span> </p> |
|
||||||
<span>09:45 AM</span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
</div> */} |
|
||||||
|
|
||||||
export default UserNotifications |
|
@ -1,116 +0,0 @@ |
|||||||
import { PaginationEllipsis, PaginationItem, PaginationLink } from "@/components/ui/pagination"; |
|
||||||
|
|
||||||
export const generatePaginationLinks = ( |
|
||||||
currentPage: number, |
|
||||||
totalPages: number, |
|
||||||
onPageChange: (page: number) => void |
|
||||||
) => { |
|
||||||
const pages: JSX.Element[] = []; |
|
||||||
|
|
||||||
// Show all pages if total pages is 6 or less
|
|
||||||
if (totalPages <= 6) { |
|
||||||
for (let i = 1; i <= totalPages; i++) { |
|
||||||
pages.push( |
|
||||||
<PaginationItem key={i}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(i)} |
|
||||||
isActive={i === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
{i} |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
} |
|
||||||
return pages; |
|
||||||
} |
|
||||||
|
|
||||||
// Always show first page
|
|
||||||
pages.push( |
|
||||||
<PaginationItem key={1}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(1)} |
|
||||||
isActive={1 === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
1 |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
|
|
||||||
// Calculate range for middle section
|
|
||||||
let startPage = Math.max(2, currentPage - 1); |
|
||||||
let endPage = Math.min(totalPages - 1, currentPage + 1); |
|
||||||
|
|
||||||
// Show ellipsis after first page if needed
|
|
||||||
if (startPage > 2) { |
|
||||||
if (startPage > 3) { |
|
||||||
pages.push(<PaginationEllipsis key="ellipsis-1" />); |
|
||||||
} |
|
||||||
// Show page 2 if we're not starting from it
|
|
||||||
if (startPage > 2) { |
|
||||||
pages.push( |
|
||||||
<PaginationItem key={2}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(2)} |
|
||||||
isActive={2 === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
2 |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Add middle pages
|
|
||||||
for (let i = startPage; i <= endPage; i++) { |
|
||||||
pages.push( |
|
||||||
<PaginationItem key={i}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(i)} |
|
||||||
isActive={i === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
{i} |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Show ellipsis before last page if needed
|
|
||||||
if (endPage < totalPages - 1) { |
|
||||||
// Show second to last page if we're not ending on it
|
|
||||||
if (endPage < totalPages - 2) { |
|
||||||
pages.push( |
|
||||||
<PaginationItem key={totalPages - 1}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(totalPages - 1)} |
|
||||||
isActive={totalPages - 1 === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
{totalPages - 1} |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
} |
|
||||||
if (endPage < totalPages - 2) { |
|
||||||
pages.push(<PaginationEllipsis key="ellipsis-2" />); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Always show last page
|
|
||||||
pages.push( |
|
||||||
<PaginationItem key={totalPages}> |
|
||||||
<PaginationLink |
|
||||||
onClick={() => onPageChange(totalPages)} |
|
||||||
isActive={totalPages === currentPage} |
|
||||||
className="cursor-pointer" |
|
||||||
> |
|
||||||
{totalPages} |
|
||||||
</PaginationLink> |
|
||||||
</PaginationItem> |
|
||||||
); |
|
||||||
|
|
||||||
return pages; |
|
||||||
}; |
|
@ -1,121 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" |
|
||||||
|
|
||||||
import { ButtonProps, buttonVariants } from "@/components/ui/button" |
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( |
|
||||||
<nav |
|
||||||
role="navigation" |
|
||||||
aria-label="pagination" |
|
||||||
className={cn("mx-auto flex w-full justify-center", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
|
|
||||||
const PaginationContent = React.forwardRef< |
|
||||||
HTMLUListElement, |
|
||||||
React.ComponentProps<"ul"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ul |
|
||||||
ref={ref} |
|
||||||
className={cn("flex flex-row items-center gap-1", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
PaginationContent.displayName = "PaginationContent" |
|
||||||
|
|
||||||
const PaginationItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentProps<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li ref={ref} className={cn("hover:cursor-pointer", className)} {...props} /> |
|
||||||
)) |
|
||||||
PaginationItem.displayName = "PaginationItem" |
|
||||||
|
|
||||||
type PaginationLinkProps = { |
|
||||||
isActive?: boolean; |
|
||||||
disabled?: boolean; |
|
||||||
} & Pick<ButtonProps, "size"> & |
|
||||||
React.ComponentProps<"a"> |
|
||||||
|
|
||||||
const PaginationLink = ({ |
|
||||||
className, |
|
||||||
isActive, |
|
||||||
size = "icon", |
|
||||||
disabled = false, |
|
||||||
...props |
|
||||||
}: PaginationLinkProps) => ( |
|
||||||
<PaginationItem> |
|
||||||
<a |
|
||||||
aria-current={isActive ? "page" : undefined} |
|
||||||
role="link" |
|
||||||
aria-disabled={disabled} |
|
||||||
className={cn( |
|
||||||
buttonVariants({ |
|
||||||
variant: isActive ? "default" : "ghost", |
|
||||||
size, |
|
||||||
}), |
|
||||||
disabled && "cursor-not-allowed pointer-events-none", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</PaginationItem> |
|
||||||
) |
|
||||||
PaginationLink.displayName = "PaginationLink" |
|
||||||
|
|
||||||
const PaginationPrevious = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to previous page" |
|
||||||
size="default" |
|
||||||
className={cn("gap-1 pl-2.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ChevronLeft className="h-4 w-4" /> |
|
||||||
<span>Previous</span> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
PaginationPrevious.displayName = "PaginationPrevious" |
|
||||||
|
|
||||||
const PaginationNext = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to next page" |
|
||||||
size="default" |
|
||||||
className={cn("gap-1 pr-2.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<span>Next</span> |
|
||||||
<ChevronRight className="h-4 w-4" /> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
|
|
||||||
const PaginationEllipsis = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"span">) => ( |
|
||||||
<span |
|
||||||
aria-hidden |
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
<span className="sr-only">More pages</span> |
|
||||||
</span> |
|
||||||
) |
|
||||||
|
|
||||||
export { |
|
||||||
Pagination, |
|
||||||
PaginationContent, |
|
||||||
PaginationEllipsis, |
|
||||||
PaginationItem, |
|
||||||
PaginationLink, |
|
||||||
PaginationNext, |
|
||||||
PaginationPrevious, |
|
||||||
} |
|
@ -1,55 +0,0 @@ |
|||||||
import { Pagination, PaginationContent, PaginationItem, PaginationNext, PaginationPrevious } from "@/components/ui/pagination"; |
|
||||||
import { generatePaginationLinks } from "./GeneratePages"; |
|
||||||
import { useRouter, useSearchParams } from "next/navigation"; |
|
||||||
|
|
||||||
type PaginatorProps = { |
|
||||||
currentPage: number; |
|
||||||
totalPages: number; |
|
||||||
showPreviousNext: boolean; |
|
||||||
mutate : () => void |
|
||||||
} |
|
||||||
|
|
||||||
export default function Paginator({ |
|
||||||
currentPage, |
|
||||||
totalPages, |
|
||||||
showPreviousNext, |
|
||||||
mutate |
|
||||||
}: PaginatorProps) { |
|
||||||
|
|
||||||
const searchParams = useSearchParams() |
|
||||||
const router = useRouter() |
|
||||||
|
|
||||||
const onPageChange = (page : number) : void => { |
|
||||||
const currentParams = new URLSearchParams(searchParams.toString()) |
|
||||||
currentParams.set( 'page' , page.toString()) |
|
||||||
router.replace(`?${currentParams.toString()}`) |
|
||||||
mutate() |
|
||||||
} |
|
||||||
return ( |
|
||||||
<div className="w-fit ml-auto mt-4"> |
|
||||||
<Pagination> |
|
||||||
<PaginationContent> |
|
||||||
{showPreviousNext && totalPages ? ( |
|
||||||
<PaginationItem> |
|
||||||
<PaginationPrevious |
|
||||||
onClick={() => onPageChange(currentPage - 1)} |
|
||||||
disabled={currentPage - 1 < 1} |
|
||||||
className="cursor-pointer" |
|
||||||
/> |
|
||||||
</PaginationItem> |
|
||||||
) : null} |
|
||||||
{generatePaginationLinks(currentPage, totalPages, onPageChange)} |
|
||||||
{showPreviousNext && totalPages ? ( |
|
||||||
<PaginationItem> |
|
||||||
<PaginationNext |
|
||||||
onClick={() => onPageChange(currentPage + 1)} |
|
||||||
disabled={currentPage > totalPages - 1} |
|
||||||
className="cursor-pointer" |
|
||||||
/> |
|
||||||
</PaginationItem> |
|
||||||
): null} |
|
||||||
</PaginationContent> |
|
||||||
</Pagination> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
@ -1,48 +0,0 @@ |
|||||||
"use client"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { SidebarContext } from "@/hooks/use-sidebar"; |
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import Image from "next/image"; |
|
||||||
import Link from "next/link"; |
|
||||||
import { useContext } from "react"; |
|
||||||
import { SidebarToggle } from "./_partials/sidebarToggle"; |
|
||||||
import { Menu } from "./_partials/menu"; |
|
||||||
|
|
||||||
export default function AppSideBar() { |
|
||||||
|
|
||||||
const sidebar = useContext(SidebarContext) |
|
||||||
if (!sidebar) return <>No Sidebar provider</>; |
|
||||||
const { isOpen, toggleOpen, getOpenState, setIsHover, settings } = sidebar; |
|
||||||
|
|
||||||
|
|
||||||
return ( |
|
||||||
<aside |
|
||||||
className={cn( |
|
||||||
"fixed top-0 left-0 z-20 h-screen -translate-x-full lg:translate-x-0 transition-[width] ease-in-out duration-300", |
|
||||||
!getOpenState() ? "w-[90px]" : "w-72", |
|
||||||
settings.disabled && "hidden" |
|
||||||
)} |
|
||||||
> |
|
||||||
<SidebarToggle isOpen={isOpen} setIsOpen={toggleOpen} /> |
|
||||||
<div |
|
||||||
onMouseEnter={() => setIsHover(true)} |
|
||||||
onMouseLeave={() => setIsHover(false)} |
|
||||||
className="relative h-full flex flex-col px-3 py-4 overflow-y-auto shadow-md dark:shadow-zinc-800" |
|
||||||
> |
|
||||||
<Button |
|
||||||
className={cn( |
|
||||||
"transition-transform ease-in-out duration-300 mb-1", |
|
||||||
!getOpenState() ? "translate-x-1" : "translate-x-0" |
|
||||||
)} |
|
||||||
variant="link" |
|
||||||
asChild |
|
||||||
> |
|
||||||
</Button> |
|
||||||
<Link href="/dashboard" className="block py-1"> |
|
||||||
<Image src={'https://www.kumarijob.com/soft-assets/images/logo.svg'} width={150} height={300} alt="brand-logo" className="mx-auto block"/> |
|
||||||
</Link> |
|
||||||
<Menu isOpen={getOpenState()} /> |
|
||||||
</div> |
|
||||||
</aside> |
|
||||||
); |
|
||||||
} |
|
@ -1,189 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import Link from "next/link"; |
|
||||||
import { useState } from "react"; |
|
||||||
import { ChevronDown, Dot, LucideIcon } from "lucide-react"; |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { DropdownMenuArrow } from "@radix-ui/react-dropdown-menu"; |
|
||||||
import { |
|
||||||
Collapsible, |
|
||||||
CollapsibleContent, |
|
||||||
CollapsibleTrigger |
|
||||||
} from "@/components/ui/collapsible"; |
|
||||||
import { |
|
||||||
Tooltip, |
|
||||||
TooltipTrigger, |
|
||||||
TooltipContent, |
|
||||||
TooltipProvider |
|
||||||
} from "@/components/ui/tooltip"; |
|
||||||
import { |
|
||||||
DropdownMenu, |
|
||||||
DropdownMenuItem, |
|
||||||
DropdownMenuLabel, |
|
||||||
DropdownMenuTrigger, |
|
||||||
DropdownMenuContent, |
|
||||||
DropdownMenuSeparator |
|
||||||
} from "@/components/ui/dropdown-menu"; |
|
||||||
import { usePathname } from "next/navigation"; |
|
||||||
|
|
||||||
type Submenu = { |
|
||||||
href: string; |
|
||||||
label: string; |
|
||||||
active?: boolean; |
|
||||||
}; |
|
||||||
|
|
||||||
interface CollapseMenuButtonProps { |
|
||||||
icon: LucideIcon; |
|
||||||
label: string; |
|
||||||
active: boolean; |
|
||||||
submenus: Submenu[]; |
|
||||||
isOpen: boolean | undefined; |
|
||||||
} |
|
||||||
|
|
||||||
export function CollapseMenuButton({ |
|
||||||
icon: Icon, |
|
||||||
label, |
|
||||||
submenus, |
|
||||||
isOpen |
|
||||||
}: CollapseMenuButtonProps) { |
|
||||||
const pathname = usePathname(); |
|
||||||
const isSubmenuActive = submenus.some((submenu) => |
|
||||||
submenu.active === undefined ? submenu.href === pathname : submenu.active |
|
||||||
); |
|
||||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(isSubmenuActive); |
|
||||||
|
|
||||||
return isOpen ? ( |
|
||||||
<Collapsible |
|
||||||
open={isCollapsed} |
|
||||||
onOpenChange={setIsCollapsed} |
|
||||||
className="w-full" |
|
||||||
> |
|
||||||
<CollapsibleTrigger |
|
||||||
className="[&[data-state=open]>div>div>svg]:rotate-180 mb-1" |
|
||||||
asChild |
|
||||||
> |
|
||||||
<Button |
|
||||||
variant={isSubmenuActive ? "secondary" : "ghost"} |
|
||||||
className="w-full justify-start h-10" |
|
||||||
> |
|
||||||
<div className="w-full items-center flex justify-between"> |
|
||||||
<div className="flex items-center"> |
|
||||||
<span className="mr-4"> |
|
||||||
<Icon size={18} /> |
|
||||||
</span> |
|
||||||
<p |
|
||||||
className={cn( |
|
||||||
"max-w-[150px] truncate", |
|
||||||
isOpen |
|
||||||
? "translate-x-0 opacity-100" |
|
||||||
: "-translate-x-96 opacity-0" |
|
||||||
)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"whitespace-nowrap", |
|
||||||
isOpen |
|
||||||
? "translate-x-0 opacity-100" |
|
||||||
: "-translate-x-96 opacity-0" |
|
||||||
)} |
|
||||||
> |
|
||||||
<ChevronDown |
|
||||||
size={18} |
|
||||||
className="transition-transform duration-200" |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</Button> |
|
||||||
</CollapsibleTrigger> |
|
||||||
<CollapsibleContent className="overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down"> |
|
||||||
{submenus.map(({ href, label, active }, index) => ( |
|
||||||
<Button |
|
||||||
key={index} |
|
||||||
variant={ |
|
||||||
(active === undefined && pathname === href) || active |
|
||||||
? "secondary" |
|
||||||
: "ghost" |
|
||||||
} |
|
||||||
className="w-full justify-start h-10 mb-1" |
|
||||||
asChild |
|
||||||
> |
|
||||||
<Link href={href}> |
|
||||||
<span className="mr-4 ml-2"> |
|
||||||
<Dot size={18} /> |
|
||||||
</span> |
|
||||||
<p |
|
||||||
className={cn( |
|
||||||
"max-w-[170px] truncate", |
|
||||||
isOpen |
|
||||||
? "translate-x-0 opacity-100" |
|
||||||
: "-translate-x-96 opacity-0" |
|
||||||
)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
</p> |
|
||||||
</Link> |
|
||||||
</Button> |
|
||||||
))} |
|
||||||
</CollapsibleContent> |
|
||||||
</Collapsible> |
|
||||||
) : ( |
|
||||||
<DropdownMenu> |
|
||||||
<TooltipProvider disableHoverableContent> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger asChild> |
|
||||||
<DropdownMenuTrigger asChild> |
|
||||||
<Button |
|
||||||
variant={isSubmenuActive ? "secondary" : "ghost"} |
|
||||||
className="w-full justify-start h-10 mb-1" |
|
||||||
> |
|
||||||
<div className="w-full items-center flex justify-between"> |
|
||||||
<div className="flex items-center"> |
|
||||||
<span className={cn(isOpen === false ? "" : "mr-4")}> |
|
||||||
<Icon size={18} /> |
|
||||||
</span> |
|
||||||
<p |
|
||||||
className={cn( |
|
||||||
"max-w-[200px] truncate", |
|
||||||
isOpen === false ? "opacity-0" : "opacity-100" |
|
||||||
)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</Button> |
|
||||||
</DropdownMenuTrigger> |
|
||||||
</TooltipTrigger> |
|
||||||
<TooltipContent side="right" align="start" alignOffset={2}> |
|
||||||
{label} |
|
||||||
</TooltipContent> |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
<DropdownMenuContent side="right" sideOffset={25} align="start"> |
|
||||||
<DropdownMenuLabel className="max-w-[190px] truncate"> |
|
||||||
{label} |
|
||||||
</DropdownMenuLabel> |
|
||||||
<DropdownMenuSeparator /> |
|
||||||
{submenus.map(({ href, label, active }, index) => ( |
|
||||||
<DropdownMenuItem key={index} asChild> |
|
||||||
<Link |
|
||||||
className={`cursor-pointer ${ |
|
||||||
((active === undefined && pathname === href) || active) && |
|
||||||
"bg-secondary" |
|
||||||
}`}
|
|
||||||
href={href} |
|
||||||
> |
|
||||||
<p className="max-w-[180px] truncate">{label}</p> |
|
||||||
</Link> |
|
||||||
</DropdownMenuItem> |
|
||||||
))} |
|
||||||
<DropdownMenuArrow className="fill-border" /> |
|
||||||
</DropdownMenuContent> |
|
||||||
</DropdownMenu> |
|
||||||
); |
|
||||||
} |
|
@ -1,152 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import Link from "next/link"; |
|
||||||
import { Ellipsis, LogOut } from "lucide-react"; |
|
||||||
import { usePathname } from "next/navigation"; |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
|
|
||||||
import { |
|
||||||
Tooltip, |
|
||||||
TooltipTrigger, |
|
||||||
TooltipContent, |
|
||||||
TooltipProvider |
|
||||||
} from "@/components/ui/tooltip"; |
|
||||||
import { useContext } from "react"; |
|
||||||
import { AuthContext } from "@/helpers/context/AuthProvider"; |
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"; |
|
||||||
import { CollapseMenuButton } from "./collapseMenuButton"; |
|
||||||
import { getMenuList } from "@/utils/menu-list"; |
|
||||||
|
|
||||||
interface MenuProps { |
|
||||||
isOpen: boolean | undefined; |
|
||||||
} |
|
||||||
|
|
||||||
export function Menu({ isOpen }: MenuProps) { |
|
||||||
|
|
||||||
const pathname = usePathname(); |
|
||||||
const menuList = getMenuList(); |
|
||||||
const { logout } = useContext(AuthContext) |
|
||||||
|
|
||||||
return ( |
|
||||||
<ScrollArea className="[&>div>div[style]]:!block"> |
|
||||||
<nav className="mt-8 h-full w-full"> |
|
||||||
<ul className="flex flex-col min-h-[calc(100vh-48px-36px-16px-32px)] lg:min-h-[calc(100vh-32px-60px-32px)] items-start space-y-1 px-2"> |
|
||||||
{menuList.map(({ groupLabel, menus }, index) => ( |
|
||||||
<li className={cn("w-full", groupLabel ? "pt-3" : "")} key={index}> |
|
||||||
{(isOpen && groupLabel) || isOpen === undefined ? ( |
|
||||||
<p className="text-sm font-medium text-muted-foreground px-4 pb-2 max-w-[248px] truncate"> |
|
||||||
{groupLabel} |
|
||||||
</p> |
|
||||||
) : !isOpen && isOpen !== undefined && groupLabel ? ( |
|
||||||
<TooltipProvider> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger className="w-full"> |
|
||||||
<div className="w-full flex justify-center items-center"> |
|
||||||
<Ellipsis className="h-5 w-5" /> |
|
||||||
</div> |
|
||||||
</TooltipTrigger> |
|
||||||
<TooltipContent side="right"> |
|
||||||
<p>{groupLabel}</p> |
|
||||||
</TooltipContent> |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
) : null} |
|
||||||
{menus.map( |
|
||||||
({ href, label, icon: Icon, active, submenus }, index) => |
|
||||||
!submenus || submenus.length === 0 ? ( |
|
||||||
<div className="w-full" key={index}> |
|
||||||
<TooltipProvider disableHoverableContent> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger asChild> |
|
||||||
<Button |
|
||||||
variant={ |
|
||||||
(active === undefined && |
|
||||||
pathname.startsWith(href)) || |
|
||||||
active |
|
||||||
? "secondary" |
|
||||||
: "ghost" |
|
||||||
} |
|
||||||
className="w-full justify-start h-10 mb-1" |
|
||||||
asChild |
|
||||||
> |
|
||||||
<Link href={href}> |
|
||||||
<span |
|
||||||
className={cn(isOpen === false ? "" : "mr-4")} |
|
||||||
> |
|
||||||
<Icon size={18} /> |
|
||||||
</span> |
|
||||||
<p |
|
||||||
className={cn( |
|
||||||
"max-w-[200px] truncate", |
|
||||||
isOpen === false |
|
||||||
? "-translate-x-96 opacity-0" |
|
||||||
: "translate-x-0 opacity-100" |
|
||||||
)} |
|
||||||
> |
|
||||||
{label} |
|
||||||
</p> |
|
||||||
</Link> |
|
||||||
</Button> |
|
||||||
</TooltipTrigger> |
|
||||||
{isOpen === false && ( |
|
||||||
<TooltipContent side="right"> |
|
||||||
{label} |
|
||||||
</TooltipContent> |
|
||||||
)} |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
</div> |
|
||||||
) : ( |
|
||||||
<div className="w-full" key={index}> |
|
||||||
{/* working */} |
|
||||||
<CollapseMenuButton |
|
||||||
icon={Icon} |
|
||||||
label={label} |
|
||||||
active={ |
|
||||||
active === undefined |
|
||||||
? pathname.startsWith(href) |
|
||||||
: active |
|
||||||
} |
|
||||||
submenus={submenus} |
|
||||||
isOpen={isOpen} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
) |
|
||||||
)} |
|
||||||
</li> |
|
||||||
))} |
|
||||||
<li className="w-full grow flex items-end"> |
|
||||||
<TooltipProvider disableHoverableContent> |
|
||||||
<Tooltip delayDuration={100}> |
|
||||||
<TooltipTrigger asChild> |
|
||||||
<Button |
|
||||||
onClick={() => {logout()}} |
|
||||||
variant="outline" |
|
||||||
className="w-full justify-center h-10 mt-5 text-red-500 hover:text-red-700" |
|
||||||
> |
|
||||||
<span className={cn(isOpen === false ? "" : "mr-2")}> |
|
||||||
<LogOut size={18} className=""/> |
|
||||||
</span> |
|
||||||
<p |
|
||||||
className={cn( |
|
||||||
"whitespace-nowrap", |
|
||||||
isOpen === false ? "opacity-0 hidden" : "opacity-100" |
|
||||||
)} |
|
||||||
> |
|
||||||
Sign out |
|
||||||
</p> |
|
||||||
</Button> |
|
||||||
</TooltipTrigger> |
|
||||||
{isOpen === false && ( |
|
||||||
<TooltipContent side="right" onClick={() => logout()}>Sign out</TooltipContent> |
|
||||||
)} |
|
||||||
</Tooltip> |
|
||||||
</TooltipProvider> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
</nav> |
|
||||||
</ScrollArea> |
|
||||||
); |
|
||||||
} |
|
@ -1,29 +0,0 @@ |
|||||||
import { ChevronLeft } from "lucide-react"; |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
|
|
||||||
interface SidebarToggleProps { |
|
||||||
isOpen: boolean | undefined; |
|
||||||
setIsOpen?: () => void; |
|
||||||
} |
|
||||||
|
|
||||||
export function SidebarToggle({ isOpen, setIsOpen }: SidebarToggleProps) { |
|
||||||
return ( |
|
||||||
<div className="invisible lg:visible absolute top-[12px] -right-[16px] z-20"> |
|
||||||
<Button |
|
||||||
onClick={() => setIsOpen?.()} |
|
||||||
className="rounded-md w-8 h-8" |
|
||||||
variant="outline" |
|
||||||
size="icon" |
|
||||||
> |
|
||||||
<ChevronLeft |
|
||||||
className={cn( |
|
||||||
"h-4 w-4 transition-transform ease-in-out duration-700", |
|
||||||
isOpen === false ? "rotate-180" : "rotate-0" |
|
||||||
)} |
|
||||||
/> |
|
||||||
</Button> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
const AdminCommonContainer: React.FC<{children : React.ReactNode , className? :string , title? : string}> = ({ |
|
||||||
children ,
|
|
||||||
className ,
|
|
||||||
title |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<> |
|
||||||
<title>{title ?? 'HRMS'}</title> |
|
||||||
<div className={`container py-4 px-4 sm:px-8 mx-auto ${className}`}>{children}</div> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default AdminCommonContainer |
|
@ -1,17 +0,0 @@ |
|||||||
const AdminContentContainer : React.FC<{ |
|
||||||
children : React.ReactNode ,
|
|
||||||
className? : string |
|
||||||
}> = ({ |
|
||||||
children ,
|
|
||||||
className |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<div className={`border bg-card text-card-foreground shadow rounded-lg border-none mt-6 min-h-[50vh] p-6 mb-16 ${className}`}> |
|
||||||
{ |
|
||||||
children |
|
||||||
} |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default AdminContentContainer |
|
@ -1,125 +0,0 @@ |
|||||||
import React, { useState, useRef, ChangeEvent } from 'react'; |
|
||||||
import { Button } from "@/components/ui/button"; |
|
||||||
import { |
|
||||||
Dialog, |
|
||||||
DialogContent, |
|
||||||
DialogHeader, |
|
||||||
DialogTitle, |
|
||||||
DialogTrigger, |
|
||||||
} from "@/components/ui/dialog"; |
|
||||||
import { Input } from "@/components/ui/input"; |
|
||||||
import { Label } from "@/components/ui/label"; |
|
||||||
import { Upload, Loader2 } from "lucide-react"; |
|
||||||
import { fetchHeader } from '@/helpers/fetch.helper'; |
|
||||||
import { useToast } from '@/hooks/use-toast'; |
|
||||||
|
|
||||||
const BulkUpload = ({ |
|
||||||
submitUrl ,
|
|
||||||
mutate |
|
||||||
} : { |
|
||||||
submitUrl : string |
|
||||||
mutate : () => void |
|
||||||
}) => { |
|
||||||
const { toast } = useToast() |
|
||||||
const [isLoading, setIsLoading] = useState(false); |
|
||||||
const [selectedFile, setSelectedFile] = useState<File | null>(null); |
|
||||||
const [open, setOpen] = useState(false); |
|
||||||
const fileInputRef = useRef<any>(null); |
|
||||||
|
|
||||||
const handleFileChange = (event : ChangeEvent<HTMLInputElement>) => { |
|
||||||
const file = event.target.files?.[0]; |
|
||||||
if (file) { |
|
||||||
setSelectedFile(file); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
const handleSubmit = async () => { |
|
||||||
if (!selectedFile) return; |
|
||||||
|
|
||||||
try { |
|
||||||
setIsLoading(true); |
|
||||||
|
|
||||||
const formData = new FormData(); |
|
||||||
formData.append('file', selectedFile); |
|
||||||
|
|
||||||
const response = await fetch(submitUrl, { |
|
||||||
method: 'POST', |
|
||||||
body: formData, |
|
||||||
headers : fetchHeader() |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
if (!response.ok) { |
|
||||||
throw new Error('Upload failed'); |
|
||||||
} |
|
||||||
const data = await response.json() |
|
||||||
|
|
||||||
// Reset and close on success
|
|
||||||
setSelectedFile(null); |
|
||||||
setOpen(false); |
|
||||||
if (fileInputRef.current) { |
|
||||||
fileInputRef.current.value = ''; |
|
||||||
} |
|
||||||
toast({ |
|
||||||
title : 'Sucessfully Uploaded !', |
|
||||||
description : data?.message, |
|
||||||
variant : 'success' |
|
||||||
}) |
|
||||||
|
|
||||||
// You might want to trigger a refresh of the employee list here
|
|
||||||
|
|
||||||
} catch (error : any) { |
|
||||||
toast({ |
|
||||||
title : 'Upload failed', |
|
||||||
description : error?.message, |
|
||||||
variant : 'destructive' |
|
||||||
}) |
|
||||||
// You might want to show an error message to the user here
|
|
||||||
} finally { |
|
||||||
setIsLoading(false); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<Dialog open={open} onOpenChange={setOpen}> |
|
||||||
<DialogTrigger asChild> |
|
||||||
<Button variant={'outline'} className='justify-center'> |
|
||||||
<Upload className="mr-2 h-4 w-4" /> |
|
||||||
Import Employees |
|
||||||
</Button> |
|
||||||
</DialogTrigger> |
|
||||||
<DialogContent> |
|
||||||
<DialogHeader> |
|
||||||
<DialogTitle>Upload Employee Data</DialogTitle> |
|
||||||
</DialogHeader> |
|
||||||
<div className="grid gap-4 py-4"> |
|
||||||
<div className="space-y-2"> |
|
||||||
<Label htmlFor="file">Select File</Label> |
|
||||||
|
|
||||||
<Input |
|
||||||
id="file" |
|
||||||
type="file" |
|
||||||
accept=".csv,.xlsx,.xls" |
|
||||||
onChange={handleFileChange} |
|
||||||
ref={fileInputRef} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
{selectedFile && ( |
|
||||||
<p className="text-sm text-muted-foreground"> |
|
||||||
Selected: {selectedFile.name} |
|
||||||
</p> |
|
||||||
)} |
|
||||||
<Button
|
|
||||||
onClick={handleSubmit} |
|
||||||
disabled={!selectedFile || isLoading} |
|
||||||
className="w-full" |
|
||||||
> |
|
||||||
Upload |
|
||||||
</Button> |
|
||||||
</div> |
|
||||||
</DialogContent> |
|
||||||
</Dialog> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default BulkUpload; |
|
@ -1,14 +0,0 @@ |
|||||||
const CommonContainer: React.FC<{children : React.ReactNode , className? :string , title? : string}> = ({ |
|
||||||
children ,
|
|
||||||
className ,
|
|
||||||
title |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<> |
|
||||||
<title>{title ?? 'HRMS'}</title> |
|
||||||
<div className={`container py-4 px-4 sm:px-8 mx-auto ${className}`}>{children}</div> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default CommonContainer |
|
@ -1,17 +0,0 @@ |
|||||||
const ContentContainer : React.FC<{ |
|
||||||
children : React.ReactNode ,
|
|
||||||
className? : string |
|
||||||
}> = ({ |
|
||||||
children ,
|
|
||||||
className |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<div className={`border bg-card text-card-foreground shadow rounded-lg border-none mt-6 min-h-[50vh] p-6 mb-16 ${className}`}> |
|
||||||
{ |
|
||||||
children |
|
||||||
} |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default ContentContainer |
|
@ -1,86 +0,0 @@ |
|||||||
import { useState } from 'react'; |
|
||||||
import { Button } from '../ui/button'; |
|
||||||
import { DownloadIcon } from 'lucide-react'; |
|
||||||
import { getEduConnectAccessToken } from '@/helpers/token.helper'; |
|
||||||
|
|
||||||
interface DownloadProps { |
|
||||||
url: string; |
|
||||||
filename?: string; |
|
||||||
className?: string; |
|
||||||
label?: string; |
|
||||||
onSuccess?: () => void; |
|
||||||
onError?: (error: Error) => void; |
|
||||||
headers?: Record<string, string>; |
|
||||||
} |
|
||||||
|
|
||||||
const Download = ({ |
|
||||||
url, |
|
||||||
filename = 'download', |
|
||||||
className = '', |
|
||||||
label = 'Download', |
|
||||||
onSuccess, |
|
||||||
onError, |
|
||||||
headers = {}, |
|
||||||
}: DownloadProps) => { |
|
||||||
const [isLoading, setIsLoading] = useState(false); |
|
||||||
|
|
||||||
const handleDownload = async () => { |
|
||||||
setIsLoading(true); |
|
||||||
try { |
|
||||||
const response = await fetch(url, { |
|
||||||
method: 'GET', |
|
||||||
headers: { |
|
||||||
Authorization : `Bearer ${getEduConnectAccessToken()}`, |
|
||||||
Accept : 'Application/json' |
|
||||||
}, |
|
||||||
}); |
|
||||||
|
|
||||||
if (!response.ok) { |
|
||||||
throw new Error(`HTTP error! status: ${response.status}`); |
|
||||||
} |
|
||||||
|
|
||||||
// Get filename from Content-Disposition header if available
|
|
||||||
const contentDisposition = response.headers.get('content-disposition'); |
|
||||||
const serverFilename = contentDisposition |
|
||||||
?.split(';') |
|
||||||
?.find(n => n.includes('filename=')) |
|
||||||
?.replace('filename=', '') |
|
||||||
?.trim(); |
|
||||||
|
|
||||||
const blob = await response.blob(); |
|
||||||
const downloadUrl = window.URL.createObjectURL(blob); |
|
||||||
const link = document.createElement('a'); |
|
||||||
link.href = downloadUrl; |
|
||||||
link.download = serverFilename || filename; |
|
||||||
|
|
||||||
// Append to document, click, and cleanup
|
|
||||||
document.body.appendChild(link); |
|
||||||
link.click(); |
|
||||||
document.body.removeChild(link); |
|
||||||
|
|
||||||
// Cleanup the URL object
|
|
||||||
window.URL.revokeObjectURL(downloadUrl); |
|
||||||
|
|
||||||
onSuccess?.(); |
|
||||||
} catch (error) { |
|
||||||
console.error('Download failed:', error); |
|
||||||
onError?.(error as Error); |
|
||||||
} finally { |
|
||||||
setIsLoading(false); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<Button |
|
||||||
onClick={handleDownload} |
|
||||||
disabled={isLoading} |
|
||||||
variant={'outline'} |
|
||||||
className={`${className} w-full`} |
|
||||||
> |
|
||||||
<DownloadIcon className="mr-2 h-4 w-4" /> |
|
||||||
{label} |
|
||||||
</Button> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default Download; |
|
@ -1,72 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Building2, Briefcase, Calendar, Edit, Trash2 } from 'lucide-react'; |
|
||||||
// import { EmployeeExperienceAPIInterface } from '@/helpers/apiSchema/employeeExperience.schema';
|
|
||||||
|
|
||||||
interface EmployeeExperienceListProps { |
|
||||||
experiences: any[]; |
|
||||||
onEdit: (experience: any) => void; |
|
||||||
onDelete: (experienceId: number) => void; |
|
||||||
} |
|
||||||
|
|
||||||
export const EmployeeExperienceList: React.FC<EmployeeExperienceListProps> = ({
|
|
||||||
experiences,
|
|
||||||
onEdit,
|
|
||||||
onDelete
|
|
||||||
}) => { |
|
||||||
return ( |
|
||||||
<ul className="employee-experience__list space-y-4"> |
|
||||||
{experiences?.map((experience) => ( |
|
||||||
<li key={experience.id} className="relative"> |
|
||||||
<div className="bg-white shadow-md rounded-lg p-4 border border-gray-200 hover:shadow-lg transition-shadow"> |
|
||||||
<div className="flex items-center mb-2"> |
|
||||||
<Building2 className="mr-3 text-blue-600" size={24} /> |
|
||||||
<h3 className="text-lg font-semibold text-gray-900"> |
|
||||||
{experience.company} |
|
||||||
</h3> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="flex items-center text-foreground"> |
|
||||||
<Briefcase className="mr-3" size={16} /> |
|
||||||
<span className="text-sm">{experience.designation}</span> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="flex items-center text-gray-600"> |
|
||||||
<Calendar className="mr-3 text-gray-500" size={16} /> |
|
||||||
<span className="text-foreground text-sm"> |
|
||||||
{new Date(experience.from_date).toLocaleDateString()} -
|
|
||||||
{new Date(experience.to_date).toLocaleDateString()} |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="mt-4 text-sm text-gray-500"> |
|
||||||
<span className="mr-2 bg-blue-100 text-blue-800 px-2 py-1 rounded"> |
|
||||||
{experience.industry} |
|
||||||
</span> |
|
||||||
<span className="bg-green-100 text-green-800 px-2 py-1 rounded"> |
|
||||||
Level {experience.job_level} |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
|
|
||||||
{/* Action Buttons */} |
|
||||||
<div className="absolute top-4 right-4 flex space-x-2"> |
|
||||||
<button
|
|
||||||
onClick={() => onEdit(experience)} |
|
||||||
className="text-yellow-500 hover:text-yellow-700" |
|
||||||
title="Edit Experience" |
|
||||||
> |
|
||||||
<Edit size={20} /> |
|
||||||
</button> |
|
||||||
<button
|
|
||||||
onClick={() => onDelete(experience.id)} |
|
||||||
className="text-red-500 hover:text-red-700" |
|
||||||
title="Delete Experience" |
|
||||||
> |
|
||||||
<Trash2 size={20} /> |
|
||||||
</button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</li> |
|
||||||
))} |
|
||||||
</ul> |
|
||||||
); |
|
||||||
}; |
|
@ -1,11 +0,0 @@ |
|||||||
const FormSection = ({ children , title } : {children : React.ReactNode , title : string}) => ( |
|
||||||
<div className="space-y-4 w-full"> |
|
||||||
<h3 className="text-lg font-semibold">{title}</h3> |
|
||||||
<div className="space-y-4"> |
|
||||||
{children} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
|
|
||||||
export default FormSection |
|
||||||
|
|
@ -1,9 +0,0 @@ |
|||||||
import { Loader2 } from "lucide-react"; |
|
||||||
|
|
||||||
const Loader: React.FC = () => { |
|
||||||
return ( |
|
||||||
<Loader2 /> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default Loader |
|
@ -1,32 +0,0 @@ |
|||||||
import React from 'react' |
|
||||||
import { Skeleton } from '../ui/skeleton' |
|
||||||
import { |
|
||||||
TableRow, |
|
||||||
TableCell, |
|
||||||
} from "@/components/ui/table" |
|
||||||
|
|
||||||
interface TableSkeletonProps { |
|
||||||
columns?: number |
|
||||||
rows?: number |
|
||||||
} |
|
||||||
|
|
||||||
export function TableSkeleton({
|
|
||||||
columns = 5, |
|
||||||
rows = 5 |
|
||||||
}: TableSkeletonProps) { |
|
||||||
return ( |
|
||||||
<> |
|
||||||
{Array.from({ length: rows }).map((_, rowIndex) => ( |
|
||||||
<TableRow key={rowIndex} className="hover:bg-transparent"> |
|
||||||
{Array.from({ length: columns }).map((_, colIndex) => ( |
|
||||||
<TableCell key={colIndex} className="p-4"> |
|
||||||
<Skeleton className="h-4 w-[80%] bg-slate-200" /> |
|
||||||
</TableCell> |
|
||||||
))} |
|
||||||
</TableRow> |
|
||||||
))} |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default TableSkeleton; |
|
@ -1,57 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as AccordionPrimitive from "@radix-ui/react-accordion" |
|
||||||
import { ChevronDown } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Accordion = AccordionPrimitive.Root |
|
||||||
|
|
||||||
const AccordionItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof AccordionPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AccordionPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn("border-b rounded-lg", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AccordionItem.displayName = "AccordionItem" |
|
||||||
|
|
||||||
const AccordionTrigger = React.forwardRef< |
|
||||||
React.ElementRef<typeof AccordionPrimitive.Trigger>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<AccordionPrimitive.Header className="flex"> |
|
||||||
<AccordionPrimitive.Trigger |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" /> |
|
||||||
</AccordionPrimitive.Trigger> |
|
||||||
</AccordionPrimitive.Header> |
|
||||||
)) |
|
||||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName |
|
||||||
|
|
||||||
const AccordionContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof AccordionPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<AccordionPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<div className={cn("pb-4 pt-0", className)}>{children}</div> |
|
||||||
</AccordionPrimitive.Content> |
|
||||||
)) |
|
||||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName |
|
||||||
|
|
||||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } |
|
@ -1,141 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { buttonVariants } from "@/components/ui/button" |
|
||||||
|
|
||||||
const AlertDialog = AlertDialogPrimitive.Root |
|
||||||
|
|
||||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger |
|
||||||
|
|
||||||
const AlertDialogPortal = AlertDialogPrimitive.Portal |
|
||||||
|
|
||||||
const AlertDialogOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Overlay |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
const AlertDialogContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPortal> |
|
||||||
<AlertDialogOverlay /> |
|
||||||
<AlertDialogPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</AlertDialogPortal> |
|
||||||
)) |
|
||||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName |
|
||||||
|
|
||||||
const AlertDialogHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-2 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
AlertDialogHeader.displayName = "AlertDialogHeader" |
|
||||||
|
|
||||||
const AlertDialogFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
AlertDialogFooter.displayName = "AlertDialogFooter" |
|
||||||
|
|
||||||
const AlertDialogTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-lg font-semibold", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName |
|
||||||
|
|
||||||
const AlertDialogDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogDescription.displayName = |
|
||||||
AlertDialogPrimitive.Description.displayName |
|
||||||
|
|
||||||
const AlertDialogAction = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Action>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Action |
|
||||||
ref={ref} |
|
||||||
className={cn(buttonVariants(), className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName |
|
||||||
|
|
||||||
const AlertDialogCancel = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Cancel |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
buttonVariants({ variant: "outline" }), |
|
||||||
"mt-2 sm:mt-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
AlertDialog, |
|
||||||
AlertDialogPortal, |
|
||||||
AlertDialogOverlay, |
|
||||||
AlertDialogTrigger, |
|
||||||
AlertDialogContent, |
|
||||||
AlertDialogHeader, |
|
||||||
AlertDialogFooter, |
|
||||||
AlertDialogTitle, |
|
||||||
AlertDialogDescription, |
|
||||||
AlertDialogAction, |
|
||||||
AlertDialogCancel, |
|
||||||
} |
|
@ -1,95 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar" |
|
||||||
import { Dialog, DialogContent } from "@/components/ui/dialog" |
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
const Avatar = React.forwardRef< |
|
||||||
React.ElementRef<typeof AvatarPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AvatarPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full cursor-pointer", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
Avatar.displayName = AvatarPrimitive.Root.displayName |
|
||||||
|
|
||||||
interface EnhancedAvatarImageProps extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> { |
|
||||||
viewerClassName?: string |
|
||||||
} |
|
||||||
|
|
||||||
const AvatarImage = React.forwardRef< |
|
||||||
React.ElementRef<typeof AvatarPrimitive.Image>, |
|
||||||
EnhancedAvatarImageProps |
|
||||||
>(({ className, viewerClassName, src, alt, ...props }, ref) => { |
|
||||||
const [isOpen, setIsOpen] = React.useState(false) |
|
||||||
|
|
||||||
console.log(isOpen) |
|
||||||
return ( |
|
||||||
<> |
|
||||||
<AvatarPrimitive.Image |
|
||||||
ref={ref} |
|
||||||
src={src} |
|
||||||
alt={alt} |
|
||||||
className={cn("aspect-square h-full w-full", className)} |
|
||||||
onClick={() => setIsOpen(true)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}> |
|
||||||
<DialogContent className="max-w-[90vw] max-h-[90vh] p-0 bg-transparent border-none" hideCloseButton> |
|
||||||
<div className="relative w-full h-full flex items-center justify-center rounded-lg overflow-hidden"> |
|
||||||
{/* Blurred background using the same image */} |
|
||||||
<div
|
|
||||||
className="absolute inset-0 bg-cover bg-center bg-no-repeat blur-xl opacity-90 scale-110" |
|
||||||
style={{ backgroundImage: `url(${src})` }} |
|
||||||
/> |
|
||||||
{/* Semi-transparent overlay to improve contrast */} |
|
||||||
<div className="absolute inset-0 bg-white/10 backdrop-blur-xl" /> |
|
||||||
|
|
||||||
<button |
|
||||||
onClick={() => setIsOpen(false)} |
|
||||||
className="absolute top-4 right-4 text-white hover:text-gray-300 focus:outline-none z-10" |
|
||||||
> |
|
||||||
<X className="h-6 w-6" /> |
|
||||||
</button> |
|
||||||
|
|
||||||
<img |
|
||||||
src={src} |
|
||||||
alt={alt} |
|
||||||
className={cn( |
|
||||||
"relative z-10 max-w-full max-h-[80vh] object-contain", |
|
||||||
viewerClassName |
|
||||||
)} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</DialogContent> |
|
||||||
</Dialog> |
|
||||||
</> |
|
||||||
) |
|
||||||
}) |
|
||||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName |
|
||||||
|
|
||||||
const AvatarFallback = React.forwardRef< |
|
||||||
React.ElementRef<typeof AvatarPrimitive.Fallback>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AvatarPrimitive.Fallback |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex h-full w-full items-center justify-center rounded-full bg-muted", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName |
|
||||||
|
|
||||||
export { Avatar, AvatarImage, AvatarFallback } |
|
@ -1,38 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const badgeVariants = cva( |
|
||||||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs cursor-pointer font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
variant: { |
|
||||||
default: |
|
||||||
"border-transparent bg-primary text-primary-foreground shadow hover:opacity-[0.95]", |
|
||||||
secondary: |
|
||||||
"border-transparent bg-secondary text-secondary-foreground hover:opacity-[0.95]", |
|
||||||
destructive: |
|
||||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:opacity-[0.95]", |
|
||||||
success :
|
|
||||||
"border-green-300 bg-green-500 text-destructive-foreground shadow hover:opacity-[0.95]", |
|
||||||
outline: "text-foreground", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
variant: "default", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
export interface BadgeProps |
|
||||||
extends React.HTMLAttributes<HTMLDivElement>, |
|
||||||
VariantProps<typeof badgeVariants> {} |
|
||||||
|
|
||||||
function Badge({ className, variant, ...props }: BadgeProps) { |
|
||||||
return ( |
|
||||||
<div className={cn(badgeVariants({ variant }), className)} {...props} /> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export { Badge, badgeVariants } |
|
@ -1,115 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { Slot } from "@radix-ui/react-slot" |
|
||||||
import { ChevronRight, MoreHorizontal } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Breadcrumb = React.forwardRef< |
|
||||||
HTMLElement, |
|
||||||
React.ComponentPropsWithoutRef<"nav"> & { |
|
||||||
separator?: React.ReactNode |
|
||||||
} |
|
||||||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) |
|
||||||
Breadcrumb.displayName = "Breadcrumb" |
|
||||||
|
|
||||||
const BreadcrumbList = React.forwardRef< |
|
||||||
HTMLOListElement, |
|
||||||
React.ComponentPropsWithoutRef<"ol"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ol |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbList.displayName = "BreadcrumbList" |
|
||||||
|
|
||||||
const BreadcrumbItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentPropsWithoutRef<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li |
|
||||||
ref={ref} |
|
||||||
className={cn("inline-flex items-center gap-1.5", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbItem.displayName = "BreadcrumbItem" |
|
||||||
|
|
||||||
const BreadcrumbLink = React.forwardRef< |
|
||||||
HTMLAnchorElement, |
|
||||||
React.ComponentPropsWithoutRef<"a"> & { |
|
||||||
asChild?: boolean |
|
||||||
} |
|
||||||
>(({ asChild, className, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "a" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
className={cn("transition-colors hover:text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
BreadcrumbLink.displayName = "BreadcrumbLink" |
|
||||||
|
|
||||||
const BreadcrumbPage = React.forwardRef< |
|
||||||
HTMLSpanElement, |
|
||||||
React.ComponentPropsWithoutRef<"span"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<span |
|
||||||
ref={ref} |
|
||||||
role="link" |
|
||||||
aria-disabled="true" |
|
||||||
aria-current="page" |
|
||||||
className={cn("font-normal text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbPage.displayName = "BreadcrumbPage" |
|
||||||
|
|
||||||
const BreadcrumbSeparator = ({ |
|
||||||
children, |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"li">) => ( |
|
||||||
<li |
|
||||||
role="presentation" |
|
||||||
aria-hidden="true" |
|
||||||
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children ?? <ChevronRight />} |
|
||||||
</li> |
|
||||||
) |
|
||||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator" |
|
||||||
|
|
||||||
const BreadcrumbEllipsis = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"span">) => ( |
|
||||||
<span |
|
||||||
role="presentation" |
|
||||||
aria-hidden="true" |
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
<span className="sr-only">More</span> |
|
||||||
</span> |
|
||||||
) |
|
||||||
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" |
|
||||||
|
|
||||||
export { |
|
||||||
Breadcrumb, |
|
||||||
BreadcrumbList, |
|
||||||
BreadcrumbItem, |
|
||||||
BreadcrumbLink, |
|
||||||
BreadcrumbPage, |
|
||||||
BreadcrumbSeparator, |
|
||||||
BreadcrumbEllipsis, |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { Slot } from "@radix-ui/react-slot" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
import { Loader, Loader2 } from "lucide-react" |
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const buttonVariants = cva( |
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
variant: { |
|
||||||
default: |
|
||||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90", |
|
||||||
destructive: |
|
||||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", |
|
||||||
outline: |
|
||||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", |
|
||||||
secondary: |
|
||||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", |
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground", |
|
||||||
link: "text-primary underline-offset-4 hover:underline", |
|
||||||
}, |
|
||||||
size: { |
|
||||||
default: "h-9 px-4 py-2", |
|
||||||
sm: "h-8 rounded-md px-3 text-xs", |
|
||||||
lg: "h-10 rounded-md px-8", |
|
||||||
icon: "h-9 w-9", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
variant: "default", |
|
||||||
size: "default", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
export interface ButtonProps |
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>, |
|
||||||
VariantProps<typeof buttonVariants> { |
|
||||||
asChild?: boolean |
|
||||||
isLoading?: boolean |
|
||||||
} |
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( |
|
||||||
({ className, variant, size, isLoading = false, asChild = false, children, disabled, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "button" |
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
className={cn(buttonVariants({ variant, size, className }))} |
|
||||||
ref={ref} |
|
||||||
disabled={isLoading || disabled} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{isLoading ? ( |
|
||||||
<> |
|
||||||
<Loader className="animate-spin" /> |
|
||||||
{children} |
|
||||||
</> |
|
||||||
) : ( |
|
||||||
children |
|
||||||
)} |
|
||||||
</Comp> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
Button.displayName = "Button" |
|
||||||
|
|
||||||
export { Button, buttonVariants } |
|
@ -1,76 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react" |
|
||||||
import { DayPicker } from "react-day-picker" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { buttonVariants } from "@/components/ui/button" |
|
||||||
|
|
||||||
export type CalendarProps = React.ComponentProps<typeof DayPicker> |
|
||||||
|
|
||||||
function Calendar({ |
|
||||||
className, |
|
||||||
classNames, |
|
||||||
showOutsideDays = true, |
|
||||||
...props |
|
||||||
}: CalendarProps) { |
|
||||||
return ( |
|
||||||
<DayPicker |
|
||||||
showOutsideDays={showOutsideDays} |
|
||||||
className={cn("p-3", className)} |
|
||||||
classNames={{ |
|
||||||
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0", |
|
||||||
month: "space-y-4", |
|
||||||
caption: "flex justify-center pt-1 relative items-center", |
|
||||||
caption_label: "text-sm font-medium", |
|
||||||
nav: "space-x-1 flex items-center", |
|
||||||
nav_button: cn( |
|
||||||
buttonVariants({ variant: "outline" }), |
|
||||||
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" |
|
||||||
), |
|
||||||
nav_button_previous: "absolute left-1", |
|
||||||
nav_button_next: "absolute right-1", |
|
||||||
table: "w-full border-collapse space-y-1", |
|
||||||
head_row: "flex", |
|
||||||
head_cell: |
|
||||||
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]", |
|
||||||
row: "flex w-full mt-2", |
|
||||||
cell: cn( |
|
||||||
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md", |
|
||||||
props.mode === "range" |
|
||||||
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" |
|
||||||
: "[&:has([aria-selected])]:rounded-md" |
|
||||||
), |
|
||||||
day: cn( |
|
||||||
buttonVariants({ variant: "ghost" }), |
|
||||||
"h-8 w-8 p-0 font-normal aria-selected:opacity-100" |
|
||||||
), |
|
||||||
day_range_start: "day-range-start", |
|
||||||
day_range_end: "day-range-end", |
|
||||||
day_selected: |
|
||||||
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", |
|
||||||
day_today: "bg-accent text-accent-foreground", |
|
||||||
day_outside: |
|
||||||
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground", |
|
||||||
day_disabled: "text-muted-foreground opacity-50", |
|
||||||
day_range_middle: |
|
||||||
"aria-selected:bg-accent aria-selected:text-accent-foreground", |
|
||||||
day_hidden: "invisible", |
|
||||||
...classNames, |
|
||||||
}} |
|
||||||
components={{ |
|
||||||
IconLeft: ({ className, ...props }) => ( |
|
||||||
<ChevronLeft className={cn("h-4 w-4", className)} {...props} /> |
|
||||||
), |
|
||||||
IconRight: ({ className, ...props }) => ( |
|
||||||
<ChevronRight className={cn("h-4 w-4", className)} {...props} /> |
|
||||||
), |
|
||||||
}} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
Calendar.displayName = "Calendar" |
|
||||||
|
|
||||||
export { Calendar } |
|
@ -1,76 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Card = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"rounded-xl border bg-card text-card-foreground shadow", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
Card.displayName = "Card" |
|
||||||
|
|
||||||
const CardHeader = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn("flex flex-col space-y-1.5 p-6", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
CardHeader.displayName = "CardHeader" |
|
||||||
|
|
||||||
const CardTitle = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn("font-semibold leading-none tracking-tight", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
CardTitle.displayName = "CardTitle" |
|
||||||
|
|
||||||
const CardDescription = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
CardDescription.displayName = "CardDescription" |
|
||||||
|
|
||||||
const CardContent = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> |
|
||||||
)) |
|
||||||
CardContent.displayName = "CardContent" |
|
||||||
|
|
||||||
const CardFooter = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn("flex items-center p-6 pt-0", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
CardFooter.displayName = "CardFooter" |
|
||||||
|
|
||||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } |
|
@ -1,365 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as RechartsPrimitive from "recharts" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
||||||
const THEMES = { light: "", dark: ".dark" } as const |
|
||||||
|
|
||||||
export type ChartConfig = { |
|
||||||
[k in string]: { |
|
||||||
label?: React.ReactNode |
|
||||||
icon?: React.ComponentType |
|
||||||
} & ( |
|
||||||
| { color?: string; theme?: never } |
|
||||||
| { color?: never; theme: Record<keyof typeof THEMES, string> } |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
type ChartContextProps = { |
|
||||||
config: ChartConfig |
|
||||||
} |
|
||||||
|
|
||||||
const ChartContext = React.createContext<ChartContextProps | null>(null) |
|
||||||
|
|
||||||
function useChart() { |
|
||||||
const context = React.useContext(ChartContext) |
|
||||||
|
|
||||||
if (!context) { |
|
||||||
throw new Error("useChart must be used within a <ChartContainer />") |
|
||||||
} |
|
||||||
|
|
||||||
return context |
|
||||||
} |
|
||||||
|
|
||||||
const ChartContainer = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & { |
|
||||||
config: ChartConfig |
|
||||||
children: React.ComponentProps< |
|
||||||
typeof RechartsPrimitive.ResponsiveContainer |
|
||||||
>["children"] |
|
||||||
} |
|
||||||
>(({ id, className, children, config, ...props }, ref) => { |
|
||||||
const uniqueId = React.useId() |
|
||||||
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` |
|
||||||
|
|
||||||
return ( |
|
||||||
<ChartContext.Provider value={{ config }}> |
|
||||||
<div |
|
||||||
data-chart={chartId} |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ChartStyle id={chartId} config={config} /> |
|
||||||
<RechartsPrimitive.ResponsiveContainer> |
|
||||||
{children} |
|
||||||
</RechartsPrimitive.ResponsiveContainer> |
|
||||||
</div> |
|
||||||
</ChartContext.Provider> |
|
||||||
) |
|
||||||
}) |
|
||||||
ChartContainer.displayName = "Chart" |
|
||||||
|
|
||||||
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { |
|
||||||
const colorConfig = Object.entries(config).filter( |
|
||||||
([, config]) => config.theme || config.color |
|
||||||
) |
|
||||||
|
|
||||||
if (!colorConfig.length) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<style |
|
||||||
dangerouslySetInnerHTML={{ |
|
||||||
__html: Object.entries(THEMES) |
|
||||||
.map( |
|
||||||
([theme, prefix]) => ` |
|
||||||
${prefix} [data-chart=${id}] { |
|
||||||
${colorConfig |
|
||||||
.map(([key, itemConfig]) => { |
|
||||||
const color = |
|
||||||
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || |
|
||||||
itemConfig.color |
|
||||||
return color ? ` --color-${key}: ${color};` : null |
|
||||||
}) |
|
||||||
.join("\n")} |
|
||||||
} |
|
||||||
` |
|
||||||
) |
|
||||||
.join("\n"), |
|
||||||
}} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
const ChartTooltip = RechartsPrimitive.Tooltip |
|
||||||
|
|
||||||
const ChartTooltipContent = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> & |
|
||||||
React.ComponentProps<"div"> & { |
|
||||||
hideLabel?: boolean |
|
||||||
hideIndicator?: boolean |
|
||||||
indicator?: "line" | "dot" | "dashed" |
|
||||||
nameKey?: string |
|
||||||
labelKey?: string |
|
||||||
} |
|
||||||
>( |
|
||||||
( |
|
||||||
{ |
|
||||||
active, |
|
||||||
payload, |
|
||||||
className, |
|
||||||
indicator = "dot", |
|
||||||
hideLabel = false, |
|
||||||
hideIndicator = false, |
|
||||||
label, |
|
||||||
labelFormatter, |
|
||||||
labelClassName, |
|
||||||
formatter, |
|
||||||
color, |
|
||||||
nameKey, |
|
||||||
labelKey, |
|
||||||
}, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const { config } = useChart() |
|
||||||
|
|
||||||
const tooltipLabel = React.useMemo(() => { |
|
||||||
if (hideLabel || !payload?.length) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
const [item] = payload |
|
||||||
const key = `${labelKey || item.dataKey || item.name || "value"}` |
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key) |
|
||||||
const value = |
|
||||||
!labelKey && typeof label === "string" |
|
||||||
? config[label as keyof typeof config]?.label || label |
|
||||||
: itemConfig?.label |
|
||||||
|
|
||||||
if (labelFormatter) { |
|
||||||
return ( |
|
||||||
<div className={cn("font-medium", labelClassName)}> |
|
||||||
{labelFormatter(value, payload)} |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
if (!value) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
return <div className={cn("font-medium", labelClassName)}>{value}</div> |
|
||||||
}, [ |
|
||||||
label, |
|
||||||
labelFormatter, |
|
||||||
payload, |
|
||||||
hideLabel, |
|
||||||
labelClassName, |
|
||||||
config, |
|
||||||
labelKey, |
|
||||||
]) |
|
||||||
|
|
||||||
if (!active || !payload?.length) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
const nestLabel = payload.length === 1 && indicator !== "dot" |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", |
|
||||||
className |
|
||||||
)} |
|
||||||
> |
|
||||||
{!nestLabel ? tooltipLabel : null} |
|
||||||
<div className="grid gap-1.5"> |
|
||||||
{payload.map((item, index) => { |
|
||||||
const key = `${nameKey || item.name || item.dataKey || "value"}` |
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key) |
|
||||||
const indicatorColor = color || item.payload.fill || item.color |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
key={item.dataKey} |
|
||||||
className={cn( |
|
||||||
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", |
|
||||||
indicator === "dot" && "items-center" |
|
||||||
)} |
|
||||||
> |
|
||||||
{formatter && item?.value !== undefined && item.name ? ( |
|
||||||
formatter(item.value, item.name, item, index, item.payload) |
|
||||||
) : ( |
|
||||||
<> |
|
||||||
{itemConfig?.icon ? ( |
|
||||||
<itemConfig.icon /> |
|
||||||
) : ( |
|
||||||
!hideIndicator && ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", |
|
||||||
{ |
|
||||||
"h-2.5 w-2.5": indicator === "dot", |
|
||||||
"w-1": indicator === "line", |
|
||||||
"w-0 border-[1.5px] border-dashed bg-transparent": |
|
||||||
indicator === "dashed", |
|
||||||
"my-0.5": nestLabel && indicator === "dashed", |
|
||||||
} |
|
||||||
)} |
|
||||||
style={ |
|
||||||
{ |
|
||||||
"--color-bg": indicatorColor, |
|
||||||
"--color-border": indicatorColor, |
|
||||||
} as React.CSSProperties |
|
||||||
} |
|
||||||
/> |
|
||||||
) |
|
||||||
)} |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-1 justify-between leading-none", |
|
||||||
nestLabel ? "items-end" : "items-center" |
|
||||||
)} |
|
||||||
> |
|
||||||
<div className="grid gap-1.5"> |
|
||||||
{nestLabel ? tooltipLabel : null} |
|
||||||
<span className="text-muted-foreground"> |
|
||||||
{itemConfig?.label || item.name} |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
{item.value && ( |
|
||||||
<span className="font-mono font-medium tabular-nums text-foreground"> |
|
||||||
{item.value.toLocaleString()} |
|
||||||
</span> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
</> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
) |
|
||||||
})} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
ChartTooltipContent.displayName = "ChartTooltip" |
|
||||||
|
|
||||||
const ChartLegend = RechartsPrimitive.Legend |
|
||||||
|
|
||||||
const ChartLegendContent = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & |
|
||||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { |
|
||||||
hideIcon?: boolean |
|
||||||
nameKey?: string |
|
||||||
} |
|
||||||
>( |
|
||||||
( |
|
||||||
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const { config } = useChart() |
|
||||||
|
|
||||||
if (!payload?.length) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex items-center justify-center gap-4", |
|
||||||
verticalAlign === "top" ? "pb-3" : "pt-3", |
|
||||||
className |
|
||||||
)} |
|
||||||
> |
|
||||||
{payload.map((item) => { |
|
||||||
const key = `${nameKey || item.dataKey || "value"}` |
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key) |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
key={item.value} |
|
||||||
className={cn( |
|
||||||
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" |
|
||||||
)} |
|
||||||
> |
|
||||||
{itemConfig?.icon && !hideIcon ? ( |
|
||||||
<itemConfig.icon /> |
|
||||||
) : ( |
|
||||||
<div |
|
||||||
className="h-2 w-2 shrink-0 rounded-[2px]" |
|
||||||
style={{ |
|
||||||
backgroundColor: item.color, |
|
||||||
}} |
|
||||||
/> |
|
||||||
)} |
|
||||||
{itemConfig?.label} |
|
||||||
</div> |
|
||||||
) |
|
||||||
})} |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
ChartLegendContent.displayName = "ChartLegend" |
|
||||||
|
|
||||||
// Helper to extract item config from a payload.
|
|
||||||
function getPayloadConfigFromPayload( |
|
||||||
config: ChartConfig, |
|
||||||
payload: unknown, |
|
||||||
key: string |
|
||||||
) { |
|
||||||
if (typeof payload !== "object" || payload === null) { |
|
||||||
return undefined |
|
||||||
} |
|
||||||
|
|
||||||
const payloadPayload = |
|
||||||
"payload" in payload && |
|
||||||
typeof payload.payload === "object" && |
|
||||||
payload.payload !== null |
|
||||||
? payload.payload |
|
||||||
: undefined |
|
||||||
|
|
||||||
let configLabelKey: string = key |
|
||||||
|
|
||||||
if ( |
|
||||||
key in payload && |
|
||||||
typeof payload[key as keyof typeof payload] === "string" |
|
||||||
) { |
|
||||||
configLabelKey = payload[key as keyof typeof payload] as string |
|
||||||
} else if ( |
|
||||||
payloadPayload && |
|
||||||
key in payloadPayload && |
|
||||||
typeof payloadPayload[key as keyof typeof payloadPayload] === "string" |
|
||||||
) { |
|
||||||
configLabelKey = payloadPayload[ |
|
||||||
key as keyof typeof payloadPayload |
|
||||||
] as string |
|
||||||
} |
|
||||||
|
|
||||||
return configLabelKey in config |
|
||||||
? config[configLabelKey] |
|
||||||
: config[key as keyof typeof config] |
|
||||||
} |
|
||||||
|
|
||||||
export { |
|
||||||
ChartContainer, |
|
||||||
ChartTooltip, |
|
||||||
ChartTooltipContent, |
|
||||||
ChartLegend, |
|
||||||
ChartLegendContent, |
|
||||||
ChartStyle, |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox" |
|
||||||
import { Check } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Checkbox = React.forwardRef< |
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CheckboxPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"peer h-4 w-4 shrink-0 rounded-sm border border-gray-300 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<CheckboxPrimitive.Indicator |
|
||||||
className={cn("flex items-center justify-center text-current")} |
|
||||||
> |
|
||||||
<Check className="h-4 w-4" /> |
|
||||||
</CheckboxPrimitive.Indicator> |
|
||||||
</CheckboxPrimitive.Root> |
|
||||||
)) |
|
||||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName |
|
||||||
|
|
||||||
export { Checkbox } |
|
@ -1,11 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" |
|
||||||
|
|
||||||
const Collapsible = CollapsiblePrimitive.Root |
|
||||||
|
|
||||||
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger |
|
||||||
|
|
||||||
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent |
|
||||||
|
|
||||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent } |
|
@ -1,153 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import { type DialogProps } from "@radix-ui/react-dialog" |
|
||||||
import { Command as CommandPrimitive } from "cmdk" |
|
||||||
import { Search } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { Dialog, DialogContent } from "@/components/ui/dialog" |
|
||||||
|
|
||||||
const Command = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CommandPrimitive |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
Command.displayName = CommandPrimitive.displayName |
|
||||||
|
|
||||||
const CommandDialog = ({ children, ...props }: DialogProps) => { |
|
||||||
return ( |
|
||||||
<Dialog {...props}> |
|
||||||
<DialogContent className="overflow-hidden p-0"> |
|
||||||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> |
|
||||||
{children} |
|
||||||
</Command> |
|
||||||
</DialogContent> |
|
||||||
</Dialog> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
const CommandInput = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.Input>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper=""> |
|
||||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> |
|
||||||
<CommandPrimitive.Input |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
)) |
|
||||||
|
|
||||||
CommandInput.displayName = CommandPrimitive.Input.displayName |
|
||||||
|
|
||||||
const CommandList = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.List>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CommandPrimitive.List |
|
||||||
ref={ref} |
|
||||||
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
|
|
||||||
CommandList.displayName = CommandPrimitive.List.displayName |
|
||||||
|
|
||||||
const CommandEmpty = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.Empty>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> |
|
||||||
>((props, ref) => ( |
|
||||||
<CommandPrimitive.Empty |
|
||||||
ref={ref} |
|
||||||
className="py-6 text-center text-sm" |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
|
|
||||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName |
|
||||||
|
|
||||||
const CommandGroup = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.Group>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CommandPrimitive.Group |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
|
|
||||||
CommandGroup.displayName = CommandPrimitive.Group.displayName |
|
||||||
|
|
||||||
const CommandSeparator = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.Separator>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CommandPrimitive.Separator |
|
||||||
ref={ref} |
|
||||||
className={cn("-mx-1 h-px bg-border", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName |
|
||||||
|
|
||||||
const CommandItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof CommandPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CommandPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
|
|
||||||
CommandItem.displayName = CommandPrimitive.Item.displayName |
|
||||||
|
|
||||||
const CommandShortcut = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLSpanElement>) => { |
|
||||||
return ( |
|
||||||
<span |
|
||||||
className={cn( |
|
||||||
"ml-auto text-xs tracking-widest text-muted-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
CommandShortcut.displayName = "CommandShortcut" |
|
||||||
|
|
||||||
export { |
|
||||||
Command, |
|
||||||
CommandDialog, |
|
||||||
CommandInput, |
|
||||||
CommandList, |
|
||||||
CommandEmpty, |
|
||||||
CommandGroup, |
|
||||||
CommandItem, |
|
||||||
CommandShortcut, |
|
||||||
CommandSeparator, |
|
||||||
} |
|
@ -1,139 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root |
|
||||||
|
|
||||||
const DialogTrigger = DialogPrimitive.Trigger |
|
||||||
|
|
||||||
const DialogPortal = DialogPrimitive.Portal |
|
||||||
|
|
||||||
const DialogClose = DialogPrimitive.Close |
|
||||||
|
|
||||||
const DialogOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Overlay |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
interface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> { |
|
||||||
hideCloseButton?: boolean |
|
||||||
disableClose?: boolean |
|
||||||
} |
|
||||||
|
|
||||||
const DialogContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Content>, |
|
||||||
DialogContentProps |
|
||||||
>(({ className, children, hideCloseButton = false, disableClose = false, ...props }, ref) => ( |
|
||||||
<DialogPortal> |
|
||||||
<DialogOverlay className={disableClose ? "pointer-events-none" : ""} /> |
|
||||||
<DialogPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
onEscapeKeyDown={(event) => { |
|
||||||
if (disableClose) { |
|
||||||
event.preventDefault() |
|
||||||
} |
|
||||||
}} |
|
||||||
onPointerDownOutside={(event) => { |
|
||||||
if (disableClose) { |
|
||||||
event.preventDefault() |
|
||||||
} |
|
||||||
}} |
|
||||||
className={cn( |
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
{!hideCloseButton && !disableClose && ( |
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
<span className="sr-only">Close</span> |
|
||||||
</DialogPrimitive.Close> |
|
||||||
)} |
|
||||||
</DialogPrimitive.Content> |
|
||||||
</DialogPortal> |
|
||||||
)) |
|
||||||
DialogContent.displayName = DialogPrimitive.Content.displayName |
|
||||||
|
|
||||||
const DialogHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-1.5 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
DialogHeader.displayName = "DialogHeader" |
|
||||||
|
|
||||||
const DialogFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
DialogFooter.displayName = "DialogFooter" |
|
||||||
|
|
||||||
const DialogTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"text-lg font-semibold leading-none tracking-tight", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogTitle.displayName = DialogPrimitive.Title.displayName |
|
||||||
|
|
||||||
const DialogDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogDescription.displayName = DialogPrimitive.Description.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
Dialog, |
|
||||||
DialogPortal, |
|
||||||
DialogOverlay, |
|
||||||
DialogTrigger, |
|
||||||
DialogClose, |
|
||||||
DialogContent, |
|
||||||
DialogHeader, |
|
||||||
DialogFooter, |
|
||||||
DialogTitle, |
|
||||||
DialogDescription, |
|
||||||
} |
|
@ -1,201 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" |
|
||||||
import { Check, ChevronRight, Circle } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const DropdownMenu = DropdownMenuPrimitive.Root |
|
||||||
|
|
||||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger |
|
||||||
|
|
||||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group |
|
||||||
|
|
||||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal |
|
||||||
|
|
||||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub |
|
||||||
|
|
||||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup |
|
||||||
|
|
||||||
const DropdownMenuSubTrigger = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { |
|
||||||
inset?: boolean |
|
||||||
} |
|
||||||
>(({ className, inset, children, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.SubTrigger |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", |
|
||||||
inset && "pl-8", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
<ChevronRight className="ml-auto" /> |
|
||||||
</DropdownMenuPrimitive.SubTrigger> |
|
||||||
)) |
|
||||||
DropdownMenuSubTrigger.displayName = |
|
||||||
DropdownMenuPrimitive.SubTrigger.displayName |
|
||||||
|
|
||||||
const DropdownMenuSubContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.SubContent |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DropdownMenuSubContent.displayName = |
|
||||||
DropdownMenuPrimitive.SubContent.displayName |
|
||||||
|
|
||||||
const DropdownMenuContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> |
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.Portal> |
|
||||||
<DropdownMenuPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
sideOffset={sideOffset} |
|
||||||
className={cn( |
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", |
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</DropdownMenuPrimitive.Portal> |
|
||||||
)) |
|
||||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName |
|
||||||
|
|
||||||
const DropdownMenuItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { |
|
||||||
inset?: boolean |
|
||||||
} |
|
||||||
>(({ className, inset, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0", |
|
||||||
inset && "pl-8", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName |
|
||||||
|
|
||||||
const DropdownMenuCheckboxItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> |
|
||||||
>(({ className, children, checked, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.CheckboxItem |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
checked={checked} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> |
|
||||||
<DropdownMenuPrimitive.ItemIndicator> |
|
||||||
<Check className="h-4 w-4" /> |
|
||||||
</DropdownMenuPrimitive.ItemIndicator> |
|
||||||
</span> |
|
||||||
{children} |
|
||||||
</DropdownMenuPrimitive.CheckboxItem> |
|
||||||
)) |
|
||||||
DropdownMenuCheckboxItem.displayName = |
|
||||||
DropdownMenuPrimitive.CheckboxItem.displayName |
|
||||||
|
|
||||||
const DropdownMenuRadioItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.RadioItem |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> |
|
||||||
<DropdownMenuPrimitive.ItemIndicator> |
|
||||||
<Circle className="h-2 w-2 fill-current" /> |
|
||||||
</DropdownMenuPrimitive.ItemIndicator> |
|
||||||
</span> |
|
||||||
{children} |
|
||||||
</DropdownMenuPrimitive.RadioItem> |
|
||||||
)) |
|
||||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName |
|
||||||
|
|
||||||
const DropdownMenuLabel = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { |
|
||||||
inset?: boolean |
|
||||||
} |
|
||||||
>(({ className, inset, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.Label |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"px-2 py-1.5 text-sm font-semibold text-center", |
|
||||||
inset && "pl-8", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName |
|
||||||
|
|
||||||
const DropdownMenuSeparator = React.forwardRef< |
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DropdownMenuPrimitive.Separator |
|
||||||
ref={ref} |
|
||||||
className={cn("-mx-1 my-1 h-px bg-muted", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName |
|
||||||
|
|
||||||
const DropdownMenuShortcut = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLSpanElement>) => { |
|
||||||
return ( |
|
||||||
<span |
|
||||||
className={cn("ml-auto text-xs tracking-widest opacity-60", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut" |
|
||||||
|
|
||||||
export { |
|
||||||
DropdownMenu, |
|
||||||
DropdownMenuTrigger, |
|
||||||
DropdownMenuContent, |
|
||||||
DropdownMenuItem, |
|
||||||
DropdownMenuCheckboxItem, |
|
||||||
DropdownMenuRadioItem, |
|
||||||
DropdownMenuLabel, |
|
||||||
DropdownMenuSeparator, |
|
||||||
DropdownMenuShortcut, |
|
||||||
DropdownMenuGroup, |
|
||||||
DropdownMenuPortal, |
|
||||||
DropdownMenuSub, |
|
||||||
DropdownMenuSubContent, |
|
||||||
DropdownMenuSubTrigger, |
|
||||||
DropdownMenuRadioGroup, |
|
||||||
} |
|
@ -1,178 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label" |
|
||||||
import { Slot } from "@radix-ui/react-slot" |
|
||||||
import { |
|
||||||
Controller, |
|
||||||
ControllerProps, |
|
||||||
FieldPath, |
|
||||||
FieldValues, |
|
||||||
FormProvider, |
|
||||||
useFormContext, |
|
||||||
} from "react-hook-form" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { Label } from "@/components/ui/label" |
|
||||||
|
|
||||||
const Form = FormProvider |
|
||||||
|
|
||||||
type FormFieldContextValue< |
|
||||||
TFieldValues extends FieldValues = FieldValues, |
|
||||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> |
|
||||||
> = { |
|
||||||
name: TName |
|
||||||
} |
|
||||||
|
|
||||||
const FormFieldContext = React.createContext<FormFieldContextValue>( |
|
||||||
{} as FormFieldContextValue |
|
||||||
) |
|
||||||
|
|
||||||
const FormField = < |
|
||||||
TFieldValues extends FieldValues = FieldValues, |
|
||||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> |
|
||||||
>({ |
|
||||||
...props |
|
||||||
}: ControllerProps<TFieldValues, TName>) => { |
|
||||||
return ( |
|
||||||
<FormFieldContext.Provider value={{ name: props.name }}> |
|
||||||
<Controller {...props} /> |
|
||||||
</FormFieldContext.Provider> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
const useFormField = () => { |
|
||||||
const fieldContext = React.useContext(FormFieldContext) |
|
||||||
const itemContext = React.useContext(FormItemContext) |
|
||||||
const { getFieldState, formState } = useFormContext() |
|
||||||
|
|
||||||
const fieldState = getFieldState(fieldContext.name, formState) |
|
||||||
|
|
||||||
if (!fieldContext) { |
|
||||||
throw new Error("useFormField should be used within <FormField>") |
|
||||||
} |
|
||||||
|
|
||||||
const { id } = itemContext |
|
||||||
|
|
||||||
return { |
|
||||||
id, |
|
||||||
name: fieldContext.name, |
|
||||||
formItemId: `${id}-form-item`, |
|
||||||
formDescriptionId: `${id}-form-item-description`, |
|
||||||
formMessageId: `${id}-form-item-message`, |
|
||||||
...fieldState, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
type FormItemContextValue = { |
|
||||||
id: string |
|
||||||
} |
|
||||||
|
|
||||||
const FormItemContext = React.createContext<FormItemContextValue>( |
|
||||||
{} as FormItemContextValue |
|
||||||
) |
|
||||||
|
|
||||||
const FormItem = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.HTMLAttributes<HTMLDivElement> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
const id = React.useId() |
|
||||||
|
|
||||||
return ( |
|
||||||
<FormItemContext.Provider value={{ id }}> |
|
||||||
<div ref={ref} className={cn("space-y-2", className)} {...props} /> |
|
||||||
</FormItemContext.Provider> |
|
||||||
) |
|
||||||
}) |
|
||||||
FormItem.displayName = "FormItem" |
|
||||||
|
|
||||||
const FormLabel = React.forwardRef< |
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
const { error, formItemId } = useFormField() |
|
||||||
|
|
||||||
return ( |
|
||||||
<Label |
|
||||||
ref={ref} |
|
||||||
className={cn(error && "text-destructive", className)} |
|
||||||
htmlFor={formItemId} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
FormLabel.displayName = "FormLabel" |
|
||||||
|
|
||||||
const FormControl = React.forwardRef< |
|
||||||
React.ElementRef<typeof Slot>, |
|
||||||
React.ComponentPropsWithoutRef<typeof Slot> |
|
||||||
>(({ ...props }, ref) => { |
|
||||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField() |
|
||||||
|
|
||||||
return ( |
|
||||||
<Slot |
|
||||||
ref={ref} |
|
||||||
id={formItemId} |
|
||||||
aria-describedby={ |
|
||||||
!error |
|
||||||
? `${formDescriptionId}` |
|
||||||
: `${formDescriptionId} ${formMessageId}` |
|
||||||
} |
|
||||||
aria-invalid={!!error} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
FormControl.displayName = "FormControl" |
|
||||||
|
|
||||||
const FormDescription = React.forwardRef< |
|
||||||
HTMLParagraphElement, |
|
||||||
React.HTMLAttributes<HTMLParagraphElement> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
const { formDescriptionId } = useFormField() |
|
||||||
|
|
||||||
return ( |
|
||||||
<p |
|
||||||
ref={ref} |
|
||||||
id={formDescriptionId} |
|
||||||
className={cn("text-[0.8rem] text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
FormDescription.displayName = "FormDescription" |
|
||||||
|
|
||||||
const FormMessage = React.forwardRef< |
|
||||||
HTMLParagraphElement, |
|
||||||
React.HTMLAttributes<HTMLParagraphElement> |
|
||||||
>(({ className, children, ...props }, ref) => { |
|
||||||
const { error, formMessageId } = useFormField() |
|
||||||
const body = error ? String(error?.message) : children |
|
||||||
|
|
||||||
if (!body) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<p |
|
||||||
ref={ref} |
|
||||||
id={formMessageId} |
|
||||||
className={cn("text-[0.8rem] font-medium text-destructive", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{body} |
|
||||||
</p> |
|
||||||
) |
|
||||||
}) |
|
||||||
FormMessage.displayName = "FormMessage" |
|
||||||
|
|
||||||
export { |
|
||||||
useFormField, |
|
||||||
Form, |
|
||||||
FormItem, |
|
||||||
FormLabel, |
|
||||||
FormControl, |
|
||||||
FormDescription, |
|
||||||
FormMessage, |
|
||||||
FormField, |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( |
|
||||||
({ className, type, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<input |
|
||||||
type={type} |
|
||||||
className={cn( |
|
||||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", |
|
||||||
className |
|
||||||
)} |
|
||||||
ref={ref} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
Input.displayName = "Input" |
|
||||||
|
|
||||||
export { Input } |
|
@ -1,26 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const labelVariants = cva( |
|
||||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" |
|
||||||
) |
|
||||||
|
|
||||||
const Label = React.forwardRef< |
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & |
|
||||||
VariantProps<typeof labelVariants> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<LabelPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn(labelVariants(), className , 'text-sm')} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
Label.displayName = LabelPrimitive.Root.displayName |
|
||||||
|
|
||||||
export { Label } |
|
@ -1,43 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
const PageLoader = () => { |
|
||||||
return ( |
|
||||||
<div className="fixed inset-0 flex items-center justify-center bg-white dark:bg-gray-900 z-50"> |
|
||||||
<div className="flex flex-col items-center justify-center space-y-8"> |
|
||||||
<div className="relative w-40"> |
|
||||||
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center"> |
|
||||||
<div className="bg-white dark:bg-gray-900 rounded-full "> |
|
||||||
<h1 className="text-xl font-bold text-primary dark:text-blue-400 animate-pulse"> |
|
||||||
KJ HRMS |
|
||||||
</h1> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="w-52 h-1.5 bg-gray-100 dark:bg-gray-800 rounded-full overflow-hidden"> |
|
||||||
<div className="h-full bg-primary animate-[loading_1.5s_ease-in-out_infinite]"></div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<style jsx>{` |
|
||||||
@keyframes loading { |
|
||||||
0% { |
|
||||||
width: 0%; |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
50% { |
|
||||||
width: 100%; |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
100% { |
|
||||||
width: 0%; |
|
||||||
margin-left: 100%; |
|
||||||
} |
|
||||||
} |
|
||||||
`}</style>
|
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default PageLoader; |
|
@ -1,133 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { ButtonProps, buttonVariants } from "@/components/ui/button" |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( |
|
||||||
<nav |
|
||||||
role="navigation" |
|
||||||
aria-label="pagination" |
|
||||||
className={cn("mx-auto flex w-full justify-center", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
Pagination.displayName = "Pagination" |
|
||||||
|
|
||||||
const PaginationContent = React.forwardRef< |
|
||||||
HTMLUListElement, |
|
||||||
React.ComponentProps<"ul"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ul |
|
||||||
ref={ref} |
|
||||||
className={cn("flex flex-row items-center gap-1", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
PaginationContent.displayName = "PaginationContent" |
|
||||||
|
|
||||||
const PaginationItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentProps<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li ref={ref} className={cn("", className)} {...props} /> |
|
||||||
)) |
|
||||||
PaginationItem.displayName = "PaginationItem" |
|
||||||
|
|
||||||
type PaginationLinkProps = { |
|
||||||
isActive?: boolean |
|
||||||
} & Pick<ButtonProps, "size"> & |
|
||||||
React.ComponentProps<"a"> |
|
||||||
|
|
||||||
const PaginationLink = ({ |
|
||||||
className, |
|
||||||
isActive, |
|
||||||
size = "icon", |
|
||||||
...props |
|
||||||
}: PaginationLinkProps) => ( |
|
||||||
<a |
|
||||||
aria-current={isActive ? "page" : undefined} |
|
||||||
className={cn( |
|
||||||
buttonVariants({ |
|
||||||
variant: isActive ? "outline" : "ghost", |
|
||||||
size, |
|
||||||
}), |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
PaginationLink.displayName = "PaginationLink" |
|
||||||
|
|
||||||
const PaginationPrevious = ({ |
|
||||||
className, |
|
||||||
disabled, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink> & { disabled?: boolean }) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to previous page" |
|
||||||
size="default" |
|
||||||
className={cn( |
|
||||||
"gap-1 pl-2.5",
|
|
||||||
disabled && "pointer-events-none opacity-50", |
|
||||||
!disabled && "cursor-pointer", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<button disabled={disabled} className="flex gap-1 items-center"> |
|
||||||
<ChevronLeft className="h-4 w-4" /> |
|
||||||
<span>Previous</span> |
|
||||||
</button> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
|
|
||||||
const PaginationNext = ({ |
|
||||||
className, |
|
||||||
disabled, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink> & { disabled?: boolean }) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to next page" |
|
||||||
size="default" |
|
||||||
className={cn( |
|
||||||
"gap-1 pr-2.5",
|
|
||||||
disabled && "pointer-events-none opacity-50", |
|
||||||
!disabled && "cursor-pointer", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<button disabled={disabled} className="flex gap-1 items-center"> |
|
||||||
<span>Next</span> |
|
||||||
<ChevronRight className="h-4 w-4" /> |
|
||||||
</button> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
|
|
||||||
const PaginationEllipsis = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"span">) => ( |
|
||||||
<span |
|
||||||
aria-hidden |
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
<span className="sr-only">More pages</span> |
|
||||||
</span> |
|
||||||
) |
|
||||||
PaginationEllipsis.displayName = "PaginationEllipsis" |
|
||||||
|
|
||||||
export { |
|
||||||
Pagination, |
|
||||||
PaginationContent, |
|
||||||
PaginationLink, |
|
||||||
PaginationItem, |
|
||||||
PaginationPrevious, |
|
||||||
PaginationNext, |
|
||||||
PaginationEllipsis, |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as PopoverPrimitive from "@radix-ui/react-popover" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Popover = PopoverPrimitive.Root |
|
||||||
|
|
||||||
const PopoverTrigger = PopoverPrimitive.Trigger |
|
||||||
|
|
||||||
const PopoverAnchor = PopoverPrimitive.Anchor |
|
||||||
|
|
||||||
const PopoverContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof PopoverPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> |
|
||||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( |
|
||||||
<PopoverPrimitive.Portal> |
|
||||||
<PopoverPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
align={align} |
|
||||||
sideOffset={sideOffset} |
|
||||||
className={cn( |
|
||||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</PopoverPrimitive.Portal> |
|
||||||
)) |
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName |
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } |
|
@ -1,44 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" |
|
||||||
import { Circle } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const RadioGroup = React.forwardRef< |
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<RadioGroupPrimitive.Root |
|
||||||
className={cn("grid gap-2", className)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName |
|
||||||
|
|
||||||
const RadioGroupItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<RadioGroupPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"aspect-square h-4 w-4 rounded-full border border-gray-300 text-gray-300 shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center"> |
|
||||||
<Circle className="h-3.5 w-3.5 fill-primary" /> |
|
||||||
</RadioGroupPrimitive.Indicator> |
|
||||||
</RadioGroupPrimitive.Item> |
|
||||||
) |
|
||||||
}) |
|
||||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName |
|
||||||
|
|
||||||
export { RadioGroup, RadioGroupItem } |
|
@ -1,48 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const ScrollArea = React.forwardRef< |
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<ScrollAreaPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn("relative overflow-hidden", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> |
|
||||||
{children} |
|
||||||
</ScrollAreaPrimitive.Viewport> |
|
||||||
<ScrollBar /> |
|
||||||
<ScrollAreaPrimitive.Corner /> |
|
||||||
</ScrollAreaPrimitive.Root> |
|
||||||
)) |
|
||||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName |
|
||||||
|
|
||||||
const ScrollBar = React.forwardRef< |
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> |
|
||||||
>(({ className, orientation = "vertical", ...props }, ref) => ( |
|
||||||
<ScrollAreaPrimitive.ScrollAreaScrollbar |
|
||||||
ref={ref} |
|
||||||
orientation={orientation} |
|
||||||
className={cn( |
|
||||||
"flex touch-none select-none transition-colors", |
|
||||||
orientation === "vertical" && |
|
||||||
"h-full w-2.5 border-l border-l-transparent p-[1px]", |
|
||||||
orientation === "horizontal" && |
|
||||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> |
|
||||||
</ScrollAreaPrimitive.ScrollAreaScrollbar> |
|
||||||
)) |
|
||||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName |
|
||||||
|
|
||||||
export { ScrollArea, ScrollBar } |
|
@ -1,48 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const ScrollArea = React.forwardRef< |
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<ScrollAreaPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn("relative overflow-hidden", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> |
|
||||||
{children} |
|
||||||
</ScrollAreaPrimitive.Viewport> |
|
||||||
<ScrollBar /> |
|
||||||
<ScrollAreaPrimitive.Corner /> |
|
||||||
</ScrollAreaPrimitive.Root> |
|
||||||
)) |
|
||||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName |
|
||||||
|
|
||||||
const ScrollBar = React.forwardRef< |
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> |
|
||||||
>(({ className, orientation = "vertical", ...props }, ref) => ( |
|
||||||
<ScrollAreaPrimitive.ScrollAreaScrollbar |
|
||||||
ref={ref} |
|
||||||
orientation={orientation} |
|
||||||
className={cn( |
|
||||||
"flex touch-none select-none transition-colors", |
|
||||||
orientation === "vertical" && |
|
||||||
"h-full w-2.5 border-l border-l-transparent p-[1px]", |
|
||||||
orientation === "horizontal" && |
|
||||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> |
|
||||||
</ScrollAreaPrimitive.ScrollAreaScrollbar> |
|
||||||
)) |
|
||||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName |
|
||||||
|
|
||||||
export { ScrollArea, ScrollBar } |
|
@ -1,159 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SelectPrimitive from "@radix-ui/react-select" |
|
||||||
import { Check, ChevronDown, ChevronUp } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Select = SelectPrimitive.Root |
|
||||||
|
|
||||||
const SelectGroup = SelectPrimitive.Group |
|
||||||
|
|
||||||
const SelectValue = SelectPrimitive.Value |
|
||||||
|
|
||||||
const SelectTrigger = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.Trigger>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.Trigger |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
<SelectPrimitive.Icon asChild> |
|
||||||
<ChevronDown className="h-4 w-4 opacity-50" /> |
|
||||||
</SelectPrimitive.Icon> |
|
||||||
</SelectPrimitive.Trigger> |
|
||||||
)) |
|
||||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName |
|
||||||
|
|
||||||
const SelectScrollUpButton = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.ScrollUpButton |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex cursor-default items-center justify-center py-1", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ChevronUp className="h-4 w-4" /> |
|
||||||
</SelectPrimitive.ScrollUpButton> |
|
||||||
)) |
|
||||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName |
|
||||||
|
|
||||||
const SelectScrollDownButton = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.ScrollDownButton |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex cursor-default items-center justify-center py-1", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ChevronDown className="h-4 w-4" /> |
|
||||||
</SelectPrimitive.ScrollDownButton> |
|
||||||
)) |
|
||||||
SelectScrollDownButton.displayName = |
|
||||||
SelectPrimitive.ScrollDownButton.displayName |
|
||||||
|
|
||||||
const SelectContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> |
|
||||||
>(({ className, children, position = "popper", ...props }, ref) => ( |
|
||||||
<SelectPrimitive.Portal> |
|
||||||
<SelectPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
position === "popper" && |
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", |
|
||||||
className |
|
||||||
)} |
|
||||||
position={position} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<SelectScrollUpButton /> |
|
||||||
<SelectPrimitive.Viewport |
|
||||||
className={cn( |
|
||||||
"p-1", |
|
||||||
position === "popper" && |
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" |
|
||||||
)} |
|
||||||
> |
|
||||||
{children} |
|
||||||
</SelectPrimitive.Viewport> |
|
||||||
<SelectScrollDownButton /> |
|
||||||
</SelectPrimitive.Content> |
|
||||||
</SelectPrimitive.Portal> |
|
||||||
)) |
|
||||||
SelectContent.displayName = SelectPrimitive.Content.displayName |
|
||||||
|
|
||||||
const SelectLabel = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.Label>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.Label |
|
||||||
ref={ref} |
|
||||||
className={cn("px-2 py-1.5 text-sm font-semibold", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SelectLabel.displayName = SelectPrimitive.Label.displayName |
|
||||||
|
|
||||||
const SelectItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> |
|
||||||
<SelectPrimitive.ItemIndicator> |
|
||||||
<Check className="h-4 w-4" /> |
|
||||||
</SelectPrimitive.ItemIndicator> |
|
||||||
</span> |
|
||||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> |
|
||||||
</SelectPrimitive.Item> |
|
||||||
)) |
|
||||||
SelectItem.displayName = SelectPrimitive.Item.displayName |
|
||||||
|
|
||||||
const SelectSeparator = React.forwardRef< |
|
||||||
React.ElementRef<typeof SelectPrimitive.Separator>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SelectPrimitive.Separator |
|
||||||
ref={ref} |
|
||||||
className={cn("-mx-1 my-1 h-px bg-muted", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
Select, |
|
||||||
SelectGroup, |
|
||||||
SelectValue, |
|
||||||
SelectTrigger, |
|
||||||
SelectContent, |
|
||||||
SelectLabel, |
|
||||||
SelectItem, |
|
||||||
SelectSeparator, |
|
||||||
SelectScrollUpButton, |
|
||||||
SelectScrollDownButton, |
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Separator = React.forwardRef< |
|
||||||
React.ElementRef<typeof SeparatorPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> |
|
||||||
>( |
|
||||||
( |
|
||||||
{ className, orientation = "horizontal", decorative = true, ...props }, |
|
||||||
ref |
|
||||||
) => ( |
|
||||||
<SeparatorPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
decorative={decorative} |
|
||||||
orientation={orientation} |
|
||||||
className={cn( |
|
||||||
"shrink-0 bg-border", |
|
||||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
) |
|
||||||
Separator.displayName = SeparatorPrimitive.Root.displayName |
|
||||||
|
|
||||||
export { Separator } |
|
@ -1,140 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Sheet = SheetPrimitive.Root |
|
||||||
|
|
||||||
const SheetTrigger = SheetPrimitive.Trigger |
|
||||||
|
|
||||||
const SheetClose = SheetPrimitive.Close |
|
||||||
|
|
||||||
const SheetPortal = SheetPrimitive.Portal |
|
||||||
|
|
||||||
const SheetOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Overlay |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
const sheetVariants = cva( |
|
||||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
side: { |
|
||||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", |
|
||||||
bottom: |
|
||||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", |
|
||||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", |
|
||||||
right: |
|
||||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
side: "right", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
interface SheetContentProps |
|
||||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, |
|
||||||
VariantProps<typeof sheetVariants> {} |
|
||||||
|
|
||||||
const SheetContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Content>, |
|
||||||
SheetContentProps |
|
||||||
>(({ side = "right", className, children, ...props }, ref) => ( |
|
||||||
<SheetPortal> |
|
||||||
<SheetOverlay /> |
|
||||||
<SheetPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn(sheetVariants({ side }), className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
<span className="sr-only">Close</span> |
|
||||||
</SheetPrimitive.Close> |
|
||||||
{children} |
|
||||||
</SheetPrimitive.Content> |
|
||||||
</SheetPortal> |
|
||||||
)) |
|
||||||
SheetContent.displayName = SheetPrimitive.Content.displayName |
|
||||||
|
|
||||||
const SheetHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-2 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
SheetHeader.displayName = "SheetHeader" |
|
||||||
|
|
||||||
const SheetFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
SheetFooter.displayName = "SheetFooter" |
|
||||||
|
|
||||||
const SheetTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-lg font-semibold text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetTitle.displayName = SheetPrimitive.Title.displayName |
|
||||||
|
|
||||||
const SheetDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetDescription.displayName = SheetPrimitive.Description.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
Sheet, |
|
||||||
SheetPortal, |
|
||||||
SheetOverlay, |
|
||||||
SheetTrigger, |
|
||||||
SheetClose, |
|
||||||
SheetContent, |
|
||||||
SheetHeader, |
|
||||||
SheetFooter, |
|
||||||
SheetTitle, |
|
||||||
SheetDescription, |
|
||||||
} |
|
@ -1,764 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import { Slot } from "@radix-ui/react-slot" |
|
||||||
import { VariantProps, cva } from "class-variance-authority" |
|
||||||
import { PanelLeft } from "lucide-react" |
|
||||||
|
|
||||||
import { useIsMobile } from "@/hooks/use-mobile" |
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { Button } from "@/components/ui/button" |
|
||||||
import { Input } from "@/components/ui/input" |
|
||||||
import { Separator } from "@/components/ui/separator" |
|
||||||
import { Sheet, SheetContent } from "@/components/ui/sheet" |
|
||||||
import { Skeleton } from "@/components/ui/skeleton" |
|
||||||
import { |
|
||||||
Tooltip, |
|
||||||
TooltipContent, |
|
||||||
TooltipProvider, |
|
||||||
TooltipTrigger, |
|
||||||
} from "@/components/ui/tooltip" |
|
||||||
|
|
||||||
const SIDEBAR_COOKIE_NAME = "sidebar:state" |
|
||||||
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 |
|
||||||
const SIDEBAR_WIDTH = "16rem" |
|
||||||
const SIDEBAR_WIDTH_MOBILE = "18rem" |
|
||||||
const SIDEBAR_WIDTH_ICON = "3rem" |
|
||||||
const SIDEBAR_KEYBOARD_SHORTCUT = "b" |
|
||||||
|
|
||||||
type SidebarContext = { |
|
||||||
state: "expanded" | "collapsed" |
|
||||||
open: boolean |
|
||||||
setOpen: (open: boolean) => void |
|
||||||
openMobile: boolean |
|
||||||
setOpenMobile: (open: boolean) => void |
|
||||||
isMobile: boolean |
|
||||||
toggleSidebar: () => void |
|
||||||
} |
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContext | null>(null) |
|
||||||
|
|
||||||
function useSidebar() { |
|
||||||
const context = React.useContext(SidebarContext) |
|
||||||
if (!context) { |
|
||||||
throw new Error("useSidebar must be used within a SidebarProvider. from sidebar") |
|
||||||
} |
|
||||||
|
|
||||||
return context |
|
||||||
} |
|
||||||
|
|
||||||
const SidebarProvider = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & { |
|
||||||
defaultOpen?: boolean |
|
||||||
open?: boolean |
|
||||||
onOpenChange?: (open: boolean) => void |
|
||||||
} |
|
||||||
>( |
|
||||||
( |
|
||||||
{ |
|
||||||
defaultOpen = true, |
|
||||||
open: openProp, |
|
||||||
onOpenChange: setOpenProp, |
|
||||||
className, |
|
||||||
style, |
|
||||||
children, |
|
||||||
...props |
|
||||||
}, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const isMobile = useIsMobile() |
|
||||||
const [openMobile, setOpenMobile] = React.useState(false) |
|
||||||
|
|
||||||
// This is the internal state of the sidebar.
|
|
||||||
// We use openProp and setOpenProp for control from outside the component.
|
|
||||||
const [_open, _setOpen] = React.useState(defaultOpen) |
|
||||||
const open = openProp ?? _open |
|
||||||
const setOpen = React.useCallback( |
|
||||||
(value: boolean | ((value: boolean) => boolean)) => { |
|
||||||
const openState = typeof value === "function" ? value(open) : value |
|
||||||
if (setOpenProp) { |
|
||||||
setOpenProp(openState) |
|
||||||
} else { |
|
||||||
_setOpen(openState) |
|
||||||
} |
|
||||||
|
|
||||||
// This sets the cookie to keep the sidebar state.
|
|
||||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` |
|
||||||
}, |
|
||||||
[setOpenProp, open] |
|
||||||
) |
|
||||||
|
|
||||||
// Helper to toggle the sidebar.
|
|
||||||
const toggleSidebar = React.useCallback(() => { |
|
||||||
return isMobile |
|
||||||
? setOpenMobile((open) => !open) |
|
||||||
: setOpen((open) => !open) |
|
||||||
}, [isMobile, setOpen, setOpenMobile]) |
|
||||||
|
|
||||||
// Adds a keyboard shortcut to toggle the sidebar.
|
|
||||||
React.useEffect(() => { |
|
||||||
const handleKeyDown = (event: KeyboardEvent) => { |
|
||||||
if ( |
|
||||||
event.key === SIDEBAR_KEYBOARD_SHORTCUT && |
|
||||||
(event.metaKey || event.ctrlKey) |
|
||||||
) { |
|
||||||
event.preventDefault() |
|
||||||
toggleSidebar() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown) |
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown) |
|
||||||
}, [toggleSidebar]) |
|
||||||
|
|
||||||
|
|
||||||
// We add a state so that we can do data-state="expanded" or "collapsed".
|
|
||||||
// This makes it easier to style the sidebar with Tailwind classes.
|
|
||||||
const state = open ? "expanded" : "collapsed" |
|
||||||
|
|
||||||
const contextValue = React.useMemo<SidebarContext>( |
|
||||||
() => ({ |
|
||||||
state, |
|
||||||
open, |
|
||||||
setOpen, |
|
||||||
isMobile, |
|
||||||
openMobile, |
|
||||||
setOpenMobile, |
|
||||||
toggleSidebar, |
|
||||||
}), |
|
||||||
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] |
|
||||||
) |
|
||||||
|
|
||||||
return ( |
|
||||||
<SidebarContext.Provider value={contextValue}> |
|
||||||
<TooltipProvider delayDuration={0}> |
|
||||||
<div |
|
||||||
style={ |
|
||||||
{ |
|
||||||
"--sidebar-width": SIDEBAR_WIDTH, |
|
||||||
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON, |
|
||||||
...style, |
|
||||||
} as React.CSSProperties |
|
||||||
} |
|
||||||
className={cn( |
|
||||||
"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar", |
|
||||||
className |
|
||||||
)} |
|
||||||
ref={ref} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
</div> |
|
||||||
</TooltipProvider> |
|
||||||
</SidebarContext.Provider> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
SidebarProvider.displayName = "SidebarProvider" |
|
||||||
|
|
||||||
const Sidebar = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & { |
|
||||||
side?: "left" | "right" |
|
||||||
variant?: "sidebar" | "floating" | "inset" |
|
||||||
collapsible?: "offcanvas" | "icon" | "none" |
|
||||||
} |
|
||||||
>( |
|
||||||
( |
|
||||||
{ |
|
||||||
side = "left", |
|
||||||
variant = "sidebar", |
|
||||||
collapsible = "offcanvas", |
|
||||||
className, |
|
||||||
children, |
|
||||||
...props |
|
||||||
}, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const { isMobile, state, openMobile, setOpenMobile } = useSidebar() |
|
||||||
|
|
||||||
if (collapsible === "none") { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
ref={ref} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
if (isMobile) { |
|
||||||
return ( |
|
||||||
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> |
|
||||||
<SheetContent |
|
||||||
data-sidebar="sidebar" |
|
||||||
data-mobile="true" |
|
||||||
className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" |
|
||||||
style={ |
|
||||||
{ |
|
||||||
"--sidebar-width": SIDEBAR_WIDTH_MOBILE, |
|
||||||
} as React.CSSProperties |
|
||||||
} |
|
||||||
side={side} |
|
||||||
> |
|
||||||
<div className="flex h-full w-full flex-col">{children}</div> |
|
||||||
</SheetContent> |
|
||||||
</Sheet> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
className="group peer hidden md:block text-sidebar-foreground" |
|
||||||
data-state={state} |
|
||||||
data-collapsible={state === "collapsed" ? collapsible : ""} |
|
||||||
data-variant={variant} |
|
||||||
data-side={side} |
|
||||||
> |
|
||||||
{/* This is what handles the sidebar gap on desktop */} |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear", |
|
||||||
"group-data-[collapsible=offcanvas]:w-0", |
|
||||||
"group-data-[side=right]:rotate-180", |
|
||||||
variant === "floating" || variant === "inset" |
|
||||||
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]" |
|
||||||
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]" |
|
||||||
)} |
|
||||||
/> |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex", |
|
||||||
side === "left" |
|
||||||
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" |
|
||||||
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", |
|
||||||
// Adjust the padding for floating and inset variants.
|
|
||||||
variant === "floating" || variant === "inset" |
|
||||||
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]" |
|
||||||
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<div |
|
||||||
data-sidebar="sidebar" |
|
||||||
className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow" |
|
||||||
> |
|
||||||
{children} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
Sidebar.displayName = "Sidebar" |
|
||||||
|
|
||||||
const SidebarTrigger = React.forwardRef< |
|
||||||
React.ElementRef<typeof Button>, |
|
||||||
React.ComponentProps<typeof Button> |
|
||||||
>(({ className, onClick, ...props }, ref) => { |
|
||||||
const { toggleSidebar } = useSidebar() |
|
||||||
|
|
||||||
return ( |
|
||||||
<Button |
|
||||||
ref={ref} |
|
||||||
data-sidebar="trigger" |
|
||||||
variant="ghost" |
|
||||||
size="icon" |
|
||||||
className={cn("h-7 w-7", className)} |
|
||||||
onClick={(event) => { |
|
||||||
onClick?.(event) |
|
||||||
toggleSidebar() |
|
||||||
}} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<PanelLeft /> |
|
||||||
<span className="sr-only">Toggle Sidebar</span> |
|
||||||
</Button> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarTrigger.displayName = "SidebarTrigger" |
|
||||||
|
|
||||||
const SidebarRail = React.forwardRef< |
|
||||||
HTMLButtonElement, |
|
||||||
React.ComponentProps<"button"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
const { toggleSidebar } = useSidebar() |
|
||||||
|
|
||||||
return ( |
|
||||||
<button |
|
||||||
ref={ref} |
|
||||||
data-sidebar="rail" |
|
||||||
aria-label="Toggle Sidebar" |
|
||||||
tabIndex={-1} |
|
||||||
onClick={toggleSidebar} |
|
||||||
title="Toggle Sidebar" |
|
||||||
className={cn( |
|
||||||
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", |
|
||||||
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize", |
|
||||||
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", |
|
||||||
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar", |
|
||||||
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", |
|
||||||
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarRail.displayName = "SidebarRail" |
|
||||||
|
|
||||||
const SidebarInset = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"main"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<main |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"relative flex min-h-svh flex-1 flex-col bg-background", |
|
||||||
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarInset.displayName = "SidebarInset" |
|
||||||
|
|
||||||
const SidebarInput = React.forwardRef< |
|
||||||
React.ElementRef<typeof Input>, |
|
||||||
React.ComponentProps<typeof Input> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<Input |
|
||||||
ref={ref} |
|
||||||
data-sidebar="input" |
|
||||||
className={cn( |
|
||||||
"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarInput.displayName = "SidebarInput" |
|
||||||
|
|
||||||
const SidebarHeader = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="header" |
|
||||||
className={cn("flex flex-col gap-2 p-2", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarHeader.displayName = "SidebarHeader" |
|
||||||
|
|
||||||
const SidebarFooter = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="footer" |
|
||||||
className={cn("flex flex-col gap-2 p-2", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarFooter.displayName = "SidebarFooter" |
|
||||||
|
|
||||||
const SidebarSeparator = React.forwardRef< |
|
||||||
React.ElementRef<typeof Separator>, |
|
||||||
React.ComponentProps<typeof Separator> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<Separator |
|
||||||
ref={ref} |
|
||||||
data-sidebar="separator" |
|
||||||
className={cn("mx-2 w-auto bg-sidebar-border", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarSeparator.displayName = "SidebarSeparator" |
|
||||||
|
|
||||||
const SidebarContent = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="content" |
|
||||||
className={cn( |
|
||||||
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarContent.displayName = "SidebarContent" |
|
||||||
|
|
||||||
const SidebarGroup = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="group" |
|
||||||
className={cn("relative flex w-full min-w-0 flex-col p-2", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarGroup.displayName = "SidebarGroup" |
|
||||||
|
|
||||||
const SidebarGroupLabel = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & { asChild?: boolean } |
|
||||||
>(({ className, asChild = false, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "div" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
data-sidebar="group-label" |
|
||||||
className={cn( |
|
||||||
"duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", |
|
||||||
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarGroupLabel.displayName = "SidebarGroupLabel" |
|
||||||
|
|
||||||
const SidebarGroupAction = React.forwardRef< |
|
||||||
HTMLButtonElement, |
|
||||||
React.ComponentProps<"button"> & { asChild?: boolean } |
|
||||||
>(({ className, asChild = false, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "button" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
data-sidebar="group-action" |
|
||||||
className={cn( |
|
||||||
"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", |
|
||||||
// Increases the hit area of the button on mobile.
|
|
||||||
"after:absolute after:-inset-2 after:md:hidden", |
|
||||||
"group-data-[collapsible=icon]:hidden", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarGroupAction.displayName = "SidebarGroupAction" |
|
||||||
|
|
||||||
const SidebarGroupContent = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="group-content" |
|
||||||
className={cn("w-full text-sm", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SidebarGroupContent.displayName = "SidebarGroupContent" |
|
||||||
|
|
||||||
const SidebarMenu = React.forwardRef< |
|
||||||
HTMLUListElement, |
|
||||||
React.ComponentProps<"ul"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ul |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu" |
|
||||||
className={cn("flex w-full min-w-0 flex-col gap-1", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SidebarMenu.displayName = "SidebarMenu" |
|
||||||
|
|
||||||
const SidebarMenuItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentProps<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-item" |
|
||||||
className={cn("group/menu-item relative", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SidebarMenuItem.displayName = "SidebarMenuItem" |
|
||||||
|
|
||||||
const sidebarMenuButtonVariants = cva( |
|
||||||
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
variant: { |
|
||||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", |
|
||||||
outline: |
|
||||||
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", |
|
||||||
}, |
|
||||||
size: { |
|
||||||
default: "h-8 text-sm", |
|
||||||
sm: "h-7 text-xs", |
|
||||||
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
variant: "default", |
|
||||||
size: "default", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
const SidebarMenuButton = React.forwardRef< |
|
||||||
HTMLButtonElement, |
|
||||||
React.ComponentProps<"button"> & { |
|
||||||
asChild?: boolean |
|
||||||
isActive?: boolean |
|
||||||
tooltip?: string | React.ComponentProps<typeof TooltipContent> |
|
||||||
} & VariantProps<typeof sidebarMenuButtonVariants> |
|
||||||
>( |
|
||||||
( |
|
||||||
{ |
|
||||||
asChild = false, |
|
||||||
isActive = false, |
|
||||||
variant = "default", |
|
||||||
size = "default", |
|
||||||
tooltip, |
|
||||||
className, |
|
||||||
...props |
|
||||||
}, |
|
||||||
ref |
|
||||||
) => { |
|
||||||
const Comp = asChild ? Slot : "button" |
|
||||||
const { isMobile, state } = useSidebar() |
|
||||||
|
|
||||||
const button = ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-button" |
|
||||||
data-size={size} |
|
||||||
data-active={isActive} |
|
||||||
className={cn(sidebarMenuButtonVariants({ variant, size }), className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
|
|
||||||
if (!tooltip) { |
|
||||||
return button |
|
||||||
} |
|
||||||
|
|
||||||
if (typeof tooltip === "string") { |
|
||||||
tooltip = { |
|
||||||
children: tooltip, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<Tooltip> |
|
||||||
<TooltipTrigger asChild>{button}</TooltipTrigger> |
|
||||||
<TooltipContent |
|
||||||
side="right" |
|
||||||
align="center" |
|
||||||
hidden={state !== "collapsed" || isMobile} |
|
||||||
{...tooltip} |
|
||||||
/> |
|
||||||
</Tooltip> |
|
||||||
) |
|
||||||
} |
|
||||||
) |
|
||||||
SidebarMenuButton.displayName = "SidebarMenuButton" |
|
||||||
|
|
||||||
const SidebarMenuAction = React.forwardRef< |
|
||||||
HTMLButtonElement, |
|
||||||
React.ComponentProps<"button"> & { |
|
||||||
asChild?: boolean |
|
||||||
showOnHover?: boolean |
|
||||||
} |
|
||||||
>(({ className, asChild = false, showOnHover = false, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "button" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-action" |
|
||||||
className={cn( |
|
||||||
"absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0", |
|
||||||
// Increases the hit area of the button on mobile.
|
|
||||||
"after:absolute after:-inset-2 after:md:hidden", |
|
||||||
"peer-data-[size=sm]/menu-button:top-1", |
|
||||||
"peer-data-[size=default]/menu-button:top-1.5", |
|
||||||
"peer-data-[size=lg]/menu-button:top-2.5", |
|
||||||
"group-data-[collapsible=icon]:hidden", |
|
||||||
showOnHover && |
|
||||||
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarMenuAction.displayName = "SidebarMenuAction" |
|
||||||
|
|
||||||
const SidebarMenuBadge = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-badge" |
|
||||||
className={cn( |
|
||||||
"absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none", |
|
||||||
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", |
|
||||||
"peer-data-[size=sm]/menu-button:top-1", |
|
||||||
"peer-data-[size=default]/menu-button:top-1.5", |
|
||||||
"peer-data-[size=lg]/menu-button:top-2.5", |
|
||||||
"group-data-[collapsible=icon]:hidden", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SidebarMenuBadge.displayName = "SidebarMenuBadge" |
|
||||||
|
|
||||||
const SidebarMenuSkeleton = React.forwardRef< |
|
||||||
HTMLDivElement, |
|
||||||
React.ComponentProps<"div"> & { |
|
||||||
showIcon?: boolean |
|
||||||
} |
|
||||||
>(({ className, showIcon = false, ...props }, ref) => { |
|
||||||
// Random width between 50 to 90%.
|
|
||||||
const width = React.useMemo(() => { |
|
||||||
return `${Math.floor(Math.random() * 40) + 50}%` |
|
||||||
}, []) |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-skeleton" |
|
||||||
className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{showIcon && ( |
|
||||||
<Skeleton |
|
||||||
className="size-4 rounded-md" |
|
||||||
data-sidebar="menu-skeleton-icon" |
|
||||||
/> |
|
||||||
)} |
|
||||||
<Skeleton |
|
||||||
className="h-4 flex-1 max-w-[--skeleton-width]" |
|
||||||
data-sidebar="menu-skeleton-text" |
|
||||||
style={ |
|
||||||
{ |
|
||||||
"--skeleton-width": width, |
|
||||||
} as React.CSSProperties |
|
||||||
} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton" |
|
||||||
|
|
||||||
const SidebarMenuSub = React.forwardRef< |
|
||||||
HTMLUListElement, |
|
||||||
React.ComponentProps<"ul"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ul |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-sub" |
|
||||||
className={cn( |
|
||||||
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", |
|
||||||
"group-data-[collapsible=icon]:hidden", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SidebarMenuSub.displayName = "SidebarMenuSub" |
|
||||||
|
|
||||||
const SidebarMenuSubItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentProps<"li"> |
|
||||||
>(({ ...props }, ref) => <li ref={ref} {...props} />) |
|
||||||
SidebarMenuSubItem.displayName = "SidebarMenuSubItem" |
|
||||||
|
|
||||||
const SidebarMenuSubButton = React.forwardRef< |
|
||||||
HTMLAnchorElement, |
|
||||||
React.ComponentProps<"a"> & { |
|
||||||
asChild?: boolean |
|
||||||
size?: "sm" | "md" |
|
||||||
isActive?: boolean |
|
||||||
} |
|
||||||
>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "a" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
data-sidebar="menu-sub-button" |
|
||||||
data-size={size} |
|
||||||
data-active={isActive} |
|
||||||
className={cn( |
|
||||||
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", |
|
||||||
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", |
|
||||||
size === "sm" && "text-xs", |
|
||||||
size === "md" && "text-sm", |
|
||||||
"group-data-[collapsible=icon]:hidden", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
SidebarMenuSubButton.displayName = "SidebarMenuSubButton" |
|
||||||
|
|
||||||
export { |
|
||||||
Sidebar, |
|
||||||
SidebarContent, |
|
||||||
SidebarFooter, |
|
||||||
SidebarGroup, |
|
||||||
SidebarGroupAction, |
|
||||||
SidebarGroupContent, |
|
||||||
SidebarGroupLabel, |
|
||||||
SidebarHeader, |
|
||||||
SidebarInput, |
|
||||||
SidebarInset, |
|
||||||
SidebarMenu, |
|
||||||
SidebarMenuAction, |
|
||||||
SidebarMenuBadge, |
|
||||||
SidebarMenuButton, |
|
||||||
SidebarMenuItem, |
|
||||||
SidebarMenuSkeleton, |
|
||||||
SidebarMenuSub, |
|
||||||
SidebarMenuSubButton, |
|
||||||
SidebarMenuSubItem, |
|
||||||
SidebarProvider, |
|
||||||
SidebarRail, |
|
||||||
SidebarSeparator, |
|
||||||
SidebarTrigger, |
|
||||||
useSidebar, |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
function Skeleton({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
className={cn("animate-pulse rounded-md bg-primary/10", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export { Skeleton } |
|
@ -1,111 +0,0 @@ |
|||||||
import React, { useRef } from "react"; |
|
||||||
import { cn } from "@/lib/utils"; |
|
||||||
import { Check } from "lucide-react"; |
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; |
|
||||||
|
|
||||||
interface Step { |
|
||||||
number: number; |
|
||||||
label: string; |
|
||||||
} |
|
||||||
|
|
||||||
interface StepperProps { |
|
||||||
steps: Step[]; |
|
||||||
currentStep: number; |
|
||||||
onStepClick: (stepNumber: number) => void; |
|
||||||
} |
|
||||||
|
|
||||||
const Stepper: React.FC<StepperProps> = ({ |
|
||||||
steps, |
|
||||||
currentStep, |
|
||||||
onStepClick, |
|
||||||
}) => { |
|
||||||
const scrollAreaRef = useRef<HTMLDivElement | null>(null); |
|
||||||
|
|
||||||
const scrollToCurrentStep = (stepIndex: number) => { |
|
||||||
const scrollArea = scrollAreaRef.current; |
|
||||||
if (scrollArea && scrollArea.children.length) { |
|
||||||
const stepElement = scrollArea.children[stepIndex] as HTMLElement; |
|
||||||
|
|
||||||
if (stepElement) { |
|
||||||
stepElement.scrollIntoView({ |
|
||||||
behavior: "smooth", |
|
||||||
block: "center", |
|
||||||
inline: "center", |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
React.useEffect(() => { |
|
||||||
scrollToCurrentStep(currentStep - 1); |
|
||||||
}, [currentStep]); |
|
||||||
|
|
||||||
return ( |
|
||||||
<ScrollArea |
|
||||||
className="w-full whitespace-nowrap rounded-md antialiased py-4" |
|
||||||
dir="ltr" |
|
||||||
style={{ |
|
||||||
WebkitFontSmoothing: 'antialiased', |
|
||||||
MozOsxFontSmoothing: 'grayscale', |
|
||||||
textRendering: 'optimizeLegibility' |
|
||||||
}} |
|
||||||
> |
|
||||||
<div className="flex items-center justify-center" ref={scrollAreaRef}> |
|
||||||
{steps.map((step, index) => ( |
|
||||||
<div |
|
||||||
key={step.number} |
|
||||||
className="flex flex-col items-center" |
|
||||||
> |
|
||||||
<div className="flex items-center"> |
|
||||||
<div className="flex flex-col items-center justify-center"> |
|
||||||
<div |
|
||||||
onClick={() => onStepClick(step.number)} |
|
||||||
className={cn( |
|
||||||
"w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold cursor-pointer transform-gpu", |
|
||||||
step.number === currentStep |
|
||||||
? "bg-primary text-primary-foreground shadow-sm" |
|
||||||
: step.number < currentStep |
|
||||||
? "bg-primary text-primary-foreground" |
|
||||||
: "bg-secondary text-secondary-foreground" |
|
||||||
)} |
|
||||||
// style={{
|
|
||||||
// backfaceVisibility: 'hidden'
|
|
||||||
// }}
|
|
||||||
> |
|
||||||
{step.number < currentStep ? ( |
|
||||||
<Check className="w-6 h-6" /> |
|
||||||
) : ( |
|
||||||
step.number |
|
||||||
)} |
|
||||||
</div> |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"mt-2 text-xs text-center w-20 antialiased", |
|
||||||
step.number === currentStep |
|
||||||
? "text-black dark:text-primary font-bold" |
|
||||||
: "text-black dark:text-primary" |
|
||||||
)} |
|
||||||
> |
|
||||||
{step.label} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
{index < steps.length - 1 && ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"w-16 h-[2px] mb-5 -mx-5", |
|
||||||
step.number <= currentStep |
|
||||||
? "bg-primary" |
|
||||||
: "bg-slate-200" |
|
||||||
)} |
|
||||||
/> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
<ScrollBar orientation="horizontal" /> |
|
||||||
</ScrollArea> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
export default Stepper; |
|
@ -1,29 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SwitchPrimitives from "@radix-ui/react-switch" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Switch = React.forwardRef< |
|
||||||
React.ElementRef<typeof SwitchPrimitives.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SwitchPrimitives.Root |
|
||||||
className={cn( |
|
||||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
> |
|
||||||
<SwitchPrimitives.Thumb |
|
||||||
className={cn( |
|
||||||
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0" |
|
||||||
)} |
|
||||||
/> |
|
||||||
</SwitchPrimitives.Root> |
|
||||||
)) |
|
||||||
Switch.displayName = SwitchPrimitives.Root.displayName |
|
||||||
|
|
||||||
export { Switch } |
|
@ -1,120 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Table = React.forwardRef< |
|
||||||
HTMLTableElement, |
|
||||||
React.HTMLAttributes<HTMLTableElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div className="relative w-full overflow-y-scroll table-scroll"> |
|
||||||
<table |
|
||||||
ref={ref} |
|
||||||
className={cn("w-full caption-bottom text-sm", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
)) |
|
||||||
Table.displayName = "Table" |
|
||||||
|
|
||||||
const TableHeader = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> |
|
||||||
)) |
|
||||||
TableHeader.displayName = "TableHeader" |
|
||||||
|
|
||||||
const TableBody = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tbody |
|
||||||
ref={ref} |
|
||||||
className={cn("[&_tr:last-child]:border-0", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableBody.displayName = "TableBody" |
|
||||||
|
|
||||||
const TableFooter = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tfoot |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableFooter.displayName = "TableFooter" |
|
||||||
|
|
||||||
const TableRow = React.forwardRef< |
|
||||||
HTMLTableRowElement, |
|
||||||
React.HTMLAttributes<HTMLTableRowElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tr |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableRow.displayName = "TableRow" |
|
||||||
|
|
||||||
const TableHead = React.forwardRef< |
|
||||||
HTMLTableCellElement, |
|
||||||
React.ThHTMLAttributes<HTMLTableCellElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<th |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableHead.displayName = "TableHead" |
|
||||||
|
|
||||||
const TableCell = React.forwardRef< |
|
||||||
HTMLTableCellElement, |
|
||||||
React.TdHTMLAttributes<HTMLTableCellElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<td |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableCell.displayName = "TableCell" |
|
||||||
|
|
||||||
const TableCaption = React.forwardRef< |
|
||||||
HTMLTableCaptionElement, |
|
||||||
React.HTMLAttributes<HTMLTableCaptionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<caption |
|
||||||
ref={ref} |
|
||||||
className={cn("mt-4 text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableCaption.displayName = "TableCaption" |
|
||||||
|
|
||||||
export { |
|
||||||
Table, |
|
||||||
TableHeader, |
|
||||||
TableBody, |
|
||||||
TableFooter, |
|
||||||
TableHead, |
|
||||||
TableRow, |
|
||||||
TableCell, |
|
||||||
TableCaption, |
|
||||||
} |
|
@ -1,55 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Tabs = TabsPrimitive.Root |
|
||||||
|
|
||||||
const TabsList = React.forwardRef< |
|
||||||
React.ElementRef<typeof TabsPrimitive.List>, |
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<TabsPrimitive.List |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TabsList.displayName = TabsPrimitive.List.displayName |
|
||||||
|
|
||||||
const TabsTrigger = React.forwardRef< |
|
||||||
React.ElementRef<typeof TabsPrimitive.Trigger>, |
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<TabsPrimitive.Trigger |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName |
|
||||||
|
|
||||||
const TabsContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof TabsPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<TabsPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TabsContent.displayName = TabsPrimitive.Content.displayName |
|
||||||
|
|
||||||
export { Tabs, TabsList, TabsTrigger, TabsContent } |
|
@ -1,22 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Textarea = React.forwardRef< |
|
||||||
HTMLTextAreaElement, |
|
||||||
React.ComponentProps<"textarea"> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<textarea |
|
||||||
className={cn( |
|
||||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", |
|
||||||
className |
|
||||||
)} |
|
||||||
ref={ref} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
Textarea.displayName = "Textarea" |
|
||||||
|
|
||||||
export { Textarea } |
|
@ -1,9 +0,0 @@ |
|||||||
export const PageHeading: React.FC<{children : React.ReactNode}> = ({ |
|
||||||
children |
|
||||||
}) => { |
|
||||||
return( |
|
||||||
<>
|
|
||||||
<h2 className="text-3xl mb-1 font-semibold capitalize">{children}</h2> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
@ -1,131 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as ToastPrimitives from "@radix-ui/react-toast" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider |
|
||||||
|
|
||||||
const ToastViewport = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Viewport>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Viewport |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName |
|
||||||
|
|
||||||
const toastVariants = cva( |
|
||||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
variant: { |
|
||||||
default: "border bg-background text-foreground", |
|
||||||
destructive: |
|
||||||
"destructive group border-destructive bg-red-500 text-white", |
|
||||||
success : 'success group border-green-500 bg-green-500 text-white' |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
variant: "default", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
const Toast = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & |
|
||||||
VariantProps<typeof toastVariants> |
|
||||||
>(({ className, variant, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<ToastPrimitives.Root |
|
||||||
ref={ref} |
|
||||||
className={cn(toastVariants({ variant }), className)} |
|
||||||
duration={2000} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
Toast.displayName = ToastPrimitives.Root.displayName |
|
||||||
|
|
||||||
const ToastAction = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Action>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Action |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastAction.displayName = ToastPrimitives.Action.displayName |
|
||||||
|
|
||||||
const ToastClose = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Close>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Close |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", |
|
||||||
className |
|
||||||
)} |
|
||||||
toast-close="" |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
</ToastPrimitives.Close> |
|
||||||
)) |
|
||||||
ToastClose.displayName = ToastPrimitives.Close.displayName |
|
||||||
|
|
||||||
const ToastTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm font-semibold [&+div]:text-xs", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastTitle.displayName = ToastPrimitives.Title.displayName |
|
||||||
|
|
||||||
const ToastDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm opacity-90", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastDescription.displayName = ToastPrimitives.Description.displayName |
|
||||||
|
|
||||||
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> |
|
||||||
|
|
||||||
type ToastActionElement = React.ReactElement<typeof ToastAction> |
|
||||||
|
|
||||||
export { |
|
||||||
type ToastProps, |
|
||||||
type ToastActionElement, |
|
||||||
ToastProvider, |
|
||||||
ToastViewport, |
|
||||||
Toast, |
|
||||||
ToastTitle, |
|
||||||
ToastDescription, |
|
||||||
ToastClose, |
|
||||||
ToastAction, |
|
||||||
} |
|
@ -1,35 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import { useToast } from "@/hooks/use-toast" |
|
||||||
import { |
|
||||||
Toast, |
|
||||||
ToastClose, |
|
||||||
ToastDescription, |
|
||||||
ToastProvider, |
|
||||||
ToastTitle, |
|
||||||
ToastViewport, |
|
||||||
} from "@/components/ui/toast" |
|
||||||
|
|
||||||
export function Toaster() { |
|
||||||
const { toasts } = useToast() |
|
||||||
|
|
||||||
return ( |
|
||||||
<ToastProvider> |
|
||||||
{toasts.map(function ({ id, title, description, action, ...props }) { |
|
||||||
return ( |
|
||||||
<Toast key={id} {...props}> |
|
||||||
<div className="grid gap-1"> |
|
||||||
{title && <ToastTitle>{title}</ToastTitle>} |
|
||||||
{description && ( |
|
||||||
<ToastDescription>{description}</ToastDescription> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
{action} |
|
||||||
<ToastClose /> |
|
||||||
</Toast> |
|
||||||
) |
|
||||||
})} |
|
||||||
<ToastViewport /> |
|
||||||
</ToastProvider> |
|
||||||
) |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const TooltipProvider = TooltipPrimitive.Provider |
|
||||||
|
|
||||||
const Tooltip = TooltipPrimitive.Root |
|
||||||
|
|
||||||
const TooltipTrigger = TooltipPrimitive.Trigger |
|
||||||
|
|
||||||
const TooltipContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof TooltipPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> |
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => ( |
|
||||||
<TooltipPrimitive.Portal> |
|
||||||
<TooltipPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
sideOffset={sideOffset} |
|
||||||
className={cn( |
|
||||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</TooltipPrimitive.Portal> |
|
||||||
)) |
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName |
|
||||||
|
|
||||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } |
|
@ -1,141 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { buttonVariants } from "@/components/ui/button" |
|
||||||
|
|
||||||
const AlertDialog = AlertDialogPrimitive.Root |
|
||||||
|
|
||||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger |
|
||||||
|
|
||||||
const AlertDialogPortal = AlertDialogPrimitive.Portal |
|
||||||
|
|
||||||
const AlertDialogOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Overlay |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
const AlertDialogContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPortal> |
|
||||||
<AlertDialogOverlay /> |
|
||||||
<AlertDialogPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</AlertDialogPortal> |
|
||||||
)) |
|
||||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName |
|
||||||
|
|
||||||
const AlertDialogHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-2 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
AlertDialogHeader.displayName = "AlertDialogHeader" |
|
||||||
|
|
||||||
const AlertDialogFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
AlertDialogFooter.displayName = "AlertDialogFooter" |
|
||||||
|
|
||||||
const AlertDialogTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-lg font-semibold", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName |
|
||||||
|
|
||||||
const AlertDialogDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogDescription.displayName = |
|
||||||
AlertDialogPrimitive.Description.displayName |
|
||||||
|
|
||||||
const AlertDialogAction = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Action>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Action |
|
||||||
ref={ref} |
|
||||||
className={cn(buttonVariants(), className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName |
|
||||||
|
|
||||||
const AlertDialogCancel = React.forwardRef< |
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>, |
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<AlertDialogPrimitive.Cancel |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
buttonVariants({ variant: "outline" }), |
|
||||||
"mt-2 sm:mt-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
AlertDialog, |
|
||||||
AlertDialogPortal, |
|
||||||
AlertDialogOverlay, |
|
||||||
AlertDialogTrigger, |
|
||||||
AlertDialogContent, |
|
||||||
AlertDialogHeader, |
|
||||||
AlertDialogFooter, |
|
||||||
AlertDialogTitle, |
|
||||||
AlertDialogDescription, |
|
||||||
AlertDialogAction, |
|
||||||
AlertDialogCancel, |
|
||||||
} |
|
@ -1,115 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { Slot } from "@radix-ui/react-slot" |
|
||||||
import { ChevronRight, MoreHorizontal } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Breadcrumb = React.forwardRef< |
|
||||||
HTMLElement, |
|
||||||
React.ComponentPropsWithoutRef<"nav"> & { |
|
||||||
separator?: React.ReactNode |
|
||||||
} |
|
||||||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) |
|
||||||
Breadcrumb.displayName = "Breadcrumb" |
|
||||||
|
|
||||||
const BreadcrumbList = React.forwardRef< |
|
||||||
HTMLOListElement, |
|
||||||
React.ComponentPropsWithoutRef<"ol"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ol |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbList.displayName = "BreadcrumbList" |
|
||||||
|
|
||||||
const BreadcrumbItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentPropsWithoutRef<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li |
|
||||||
ref={ref} |
|
||||||
className={cn("inline-flex items-center gap-1.5", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbItem.displayName = "BreadcrumbItem" |
|
||||||
|
|
||||||
const BreadcrumbLink = React.forwardRef< |
|
||||||
HTMLAnchorElement, |
|
||||||
React.ComponentPropsWithoutRef<"a"> & { |
|
||||||
asChild?: boolean |
|
||||||
} |
|
||||||
>(({ asChild, className, ...props }, ref) => { |
|
||||||
const Comp = asChild ? Slot : "a" |
|
||||||
|
|
||||||
return ( |
|
||||||
<Comp |
|
||||||
ref={ref} |
|
||||||
className={cn("transition-colors hover:text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
BreadcrumbLink.displayName = "BreadcrumbLink" |
|
||||||
|
|
||||||
const BreadcrumbPage = React.forwardRef< |
|
||||||
HTMLSpanElement, |
|
||||||
React.ComponentPropsWithoutRef<"span"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<span |
|
||||||
ref={ref} |
|
||||||
role="link" |
|
||||||
aria-disabled="true" |
|
||||||
aria-current="page" |
|
||||||
className={cn("font-normal text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
BreadcrumbPage.displayName = "BreadcrumbPage" |
|
||||||
|
|
||||||
const BreadcrumbSeparator = ({ |
|
||||||
children, |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"li">) => ( |
|
||||||
<li |
|
||||||
role="presentation" |
|
||||||
aria-hidden="true" |
|
||||||
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children ?? <ChevronRight />} |
|
||||||
</li> |
|
||||||
) |
|
||||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator" |
|
||||||
|
|
||||||
const BreadcrumbEllipsis = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"span">) => ( |
|
||||||
<span |
|
||||||
role="presentation" |
|
||||||
aria-hidden="true" |
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
<span className="sr-only">More</span> |
|
||||||
</span> |
|
||||||
) |
|
||||||
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" |
|
||||||
|
|
||||||
export { |
|
||||||
Breadcrumb, |
|
||||||
BreadcrumbList, |
|
||||||
BreadcrumbItem, |
|
||||||
BreadcrumbLink, |
|
||||||
BreadcrumbPage, |
|
||||||
BreadcrumbSeparator, |
|
||||||
BreadcrumbEllipsis, |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox" |
|
||||||
import { Check } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Checkbox = React.forwardRef< |
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<CheckboxPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<CheckboxPrimitive.Indicator |
|
||||||
className={cn("flex items-center justify-center text-current")} |
|
||||||
> |
|
||||||
<Check className="h-4 w-4" /> |
|
||||||
</CheckboxPrimitive.Indicator> |
|
||||||
</CheckboxPrimitive.Root> |
|
||||||
)) |
|
||||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName |
|
||||||
|
|
||||||
export { Checkbox } |
|
@ -1,11 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" |
|
||||||
|
|
||||||
const Collapsible = CollapsiblePrimitive.Root |
|
||||||
|
|
||||||
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger |
|
||||||
|
|
||||||
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent |
|
||||||
|
|
||||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent } |
|
@ -1,122 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root |
|
||||||
|
|
||||||
const DialogTrigger = DialogPrimitive.Trigger |
|
||||||
|
|
||||||
const DialogPortal = DialogPrimitive.Portal |
|
||||||
|
|
||||||
const DialogClose = DialogPrimitive.Close |
|
||||||
|
|
||||||
const DialogOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Overlay |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
const DialogContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> |
|
||||||
>(({ className, children, ...props }, ref) => ( |
|
||||||
<DialogPortal> |
|
||||||
<DialogOverlay /> |
|
||||||
<DialogPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
{children} |
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
<span className="sr-only">Close</span> |
|
||||||
</DialogPrimitive.Close> |
|
||||||
</DialogPrimitive.Content> |
|
||||||
</DialogPortal> |
|
||||||
)) |
|
||||||
DialogContent.displayName = DialogPrimitive.Content.displayName |
|
||||||
|
|
||||||
const DialogHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-1.5 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
DialogHeader.displayName = "DialogHeader" |
|
||||||
|
|
||||||
const DialogFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
DialogFooter.displayName = "DialogFooter" |
|
||||||
|
|
||||||
const DialogTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"text-lg font-semibold leading-none tracking-tight", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogTitle.displayName = DialogPrimitive.Title.displayName |
|
||||||
|
|
||||||
const DialogDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof DialogPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<DialogPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
DialogDescription.displayName = DialogPrimitive.Description.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
Dialog, |
|
||||||
DialogPortal, |
|
||||||
DialogOverlay, |
|
||||||
DialogTrigger, |
|
||||||
DialogClose, |
|
||||||
DialogContent, |
|
||||||
DialogHeader, |
|
||||||
DialogFooter, |
|
||||||
DialogTitle, |
|
||||||
DialogDescription, |
|
||||||
} |
|
@ -1,117 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
import { ButtonProps, buttonVariants } from "@/components/ui/button" |
|
||||||
|
|
||||||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( |
|
||||||
<nav |
|
||||||
role="navigation" |
|
||||||
aria-label="pagination" |
|
||||||
className={cn("mx-auto flex w-full justify-center", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
Pagination.displayName = "Pagination" |
|
||||||
|
|
||||||
const PaginationContent = React.forwardRef< |
|
||||||
HTMLUListElement, |
|
||||||
React.ComponentProps<"ul"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ul |
|
||||||
ref={ref} |
|
||||||
className={cn("flex flex-row items-center gap-1", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
PaginationContent.displayName = "PaginationContent" |
|
||||||
|
|
||||||
const PaginationItem = React.forwardRef< |
|
||||||
HTMLLIElement, |
|
||||||
React.ComponentProps<"li"> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<li ref={ref} className={cn("", className)} {...props} /> |
|
||||||
)) |
|
||||||
PaginationItem.displayName = "PaginationItem" |
|
||||||
|
|
||||||
type PaginationLinkProps = { |
|
||||||
isActive?: boolean |
|
||||||
} & Pick<ButtonProps, "size"> & |
|
||||||
React.ComponentProps<"a"> |
|
||||||
|
|
||||||
const PaginationLink = ({ |
|
||||||
className, |
|
||||||
isActive, |
|
||||||
size = "icon", |
|
||||||
...props |
|
||||||
}: PaginationLinkProps) => ( |
|
||||||
<a |
|
||||||
aria-current={isActive ? "page" : undefined} |
|
||||||
className={cn( |
|
||||||
buttonVariants({ |
|
||||||
variant: isActive ? "outline" : "ghost", |
|
||||||
size, |
|
||||||
}), |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
PaginationLink.displayName = "PaginationLink" |
|
||||||
|
|
||||||
const PaginationPrevious = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to previous page" |
|
||||||
size="default" |
|
||||||
className={cn("gap-1 pl-2.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<ChevronLeft className="h-4 w-4" /> |
|
||||||
<span>Previous</span> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
PaginationPrevious.displayName = "PaginationPrevious" |
|
||||||
|
|
||||||
const PaginationNext = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => ( |
|
||||||
<PaginationLink |
|
||||||
aria-label="Go to next page" |
|
||||||
size="default" |
|
||||||
className={cn("gap-1 pr-2.5", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<span>Next</span> |
|
||||||
<ChevronRight className="h-4 w-4" /> |
|
||||||
</PaginationLink> |
|
||||||
) |
|
||||||
PaginationNext.displayName = "PaginationNext" |
|
||||||
|
|
||||||
const PaginationEllipsis = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.ComponentProps<"span">) => ( |
|
||||||
<span |
|
||||||
aria-hidden |
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<MoreHorizontal className="h-4 w-4" /> |
|
||||||
<span className="sr-only">More pages</span> |
|
||||||
</span> |
|
||||||
) |
|
||||||
PaginationEllipsis.displayName = "PaginationEllipsis" |
|
||||||
|
|
||||||
export { |
|
||||||
Pagination, |
|
||||||
PaginationContent, |
|
||||||
PaginationLink, |
|
||||||
PaginationItem, |
|
||||||
PaginationPrevious, |
|
||||||
PaginationNext, |
|
||||||
PaginationEllipsis, |
|
||||||
} |
|
@ -1,44 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" |
|
||||||
import { Circle } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const RadioGroup = React.forwardRef< |
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<RadioGroupPrimitive.Root |
|
||||||
className={cn("grid gap-2", className)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName |
|
||||||
|
|
||||||
const RadioGroupItem = React.forwardRef< |
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Item>, |
|
||||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> |
|
||||||
>(({ className, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<RadioGroupPrimitive.Item |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"aspect-square h-4 w-4 rounded-full border border-purple-700 text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center"> |
|
||||||
<Circle className="h-3.5 w-3.5 fill-purple-700/50" /> |
|
||||||
</RadioGroupPrimitive.Indicator> |
|
||||||
</RadioGroupPrimitive.Item> |
|
||||||
) |
|
||||||
}) |
|
||||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName |
|
||||||
|
|
||||||
export { RadioGroup, RadioGroupItem } |
|
@ -1,31 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Separator = React.forwardRef< |
|
||||||
React.ElementRef<typeof SeparatorPrimitive.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> |
|
||||||
>( |
|
||||||
( |
|
||||||
{ className, orientation = "horizontal", decorative = true, ...props }, |
|
||||||
ref |
|
||||||
) => ( |
|
||||||
<SeparatorPrimitive.Root |
|
||||||
ref={ref} |
|
||||||
decorative={decorative} |
|
||||||
orientation={orientation} |
|
||||||
className={cn( |
|
||||||
"shrink-0 bg-border", |
|
||||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
) |
|
||||||
Separator.displayName = SeparatorPrimitive.Root.displayName |
|
||||||
|
|
||||||
export { Separator } |
|
@ -1,140 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Sheet = SheetPrimitive.Root |
|
||||||
|
|
||||||
const SheetTrigger = SheetPrimitive.Trigger |
|
||||||
|
|
||||||
const SheetClose = SheetPrimitive.Close |
|
||||||
|
|
||||||
const SheetPortal = SheetPrimitive.Portal |
|
||||||
|
|
||||||
const SheetOverlay = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Overlay>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Overlay |
|
||||||
className={cn( |
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
ref={ref} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName |
|
||||||
|
|
||||||
const sheetVariants = cva( |
|
||||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
side: { |
|
||||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", |
|
||||||
bottom: |
|
||||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", |
|
||||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", |
|
||||||
right: |
|
||||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
side: "right", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
interface SheetContentProps |
|
||||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, |
|
||||||
VariantProps<typeof sheetVariants> {} |
|
||||||
|
|
||||||
const SheetContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Content>, |
|
||||||
SheetContentProps |
|
||||||
>(({ side = "right", className, children, ...props }, ref) => ( |
|
||||||
<SheetPortal> |
|
||||||
<SheetOverlay /> |
|
||||||
<SheetPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
className={cn(sheetVariants({ side }), className)} |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
<span className="sr-only">Close</span> |
|
||||||
</SheetPrimitive.Close> |
|
||||||
{children} |
|
||||||
</SheetPrimitive.Content> |
|
||||||
</SheetPortal> |
|
||||||
)) |
|
||||||
SheetContent.displayName = SheetPrimitive.Content.displayName |
|
||||||
|
|
||||||
const SheetHeader = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col space-y-2 text-center sm:text-left", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
SheetHeader.displayName = "SheetHeader" |
|
||||||
|
|
||||||
const SheetFooter = ({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => ( |
|
||||||
<div |
|
||||||
className={cn( |
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
SheetFooter.displayName = "SheetFooter" |
|
||||||
|
|
||||||
const SheetTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-lg font-semibold text-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetTitle.displayName = SheetPrimitive.Title.displayName |
|
||||||
|
|
||||||
const SheetDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof SheetPrimitive.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<SheetPrimitive.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
SheetDescription.displayName = SheetPrimitive.Description.displayName |
|
||||||
|
|
||||||
export { |
|
||||||
Sheet, |
|
||||||
SheetPortal, |
|
||||||
SheetOverlay, |
|
||||||
SheetTrigger, |
|
||||||
SheetClose, |
|
||||||
SheetContent, |
|
||||||
SheetHeader, |
|
||||||
SheetFooter, |
|
||||||
SheetTitle, |
|
||||||
SheetDescription, |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
function Skeleton({ |
|
||||||
className, |
|
||||||
...props |
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
className={cn("animate-pulse rounded-md bg-primary/10", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export { Skeleton } |
|
@ -1,120 +0,0 @@ |
|||||||
import * as React from "react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const Table = React.forwardRef< |
|
||||||
HTMLTableElement, |
|
||||||
React.HTMLAttributes<HTMLTableElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<div className="relative w-full overflow-auto"> |
|
||||||
<table |
|
||||||
ref={ref} |
|
||||||
className={cn("w-full caption-bottom text-sm", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
)) |
|
||||||
Table.displayName = "Table" |
|
||||||
|
|
||||||
const TableHeader = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> |
|
||||||
)) |
|
||||||
TableHeader.displayName = "TableHeader" |
|
||||||
|
|
||||||
const TableBody = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tbody |
|
||||||
ref={ref} |
|
||||||
className={cn("[&_tr:last-child]:border-0", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableBody.displayName = "TableBody" |
|
||||||
|
|
||||||
const TableFooter = React.forwardRef< |
|
||||||
HTMLTableSectionElement, |
|
||||||
React.HTMLAttributes<HTMLTableSectionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tfoot |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableFooter.displayName = "TableFooter" |
|
||||||
|
|
||||||
const TableRow = React.forwardRef< |
|
||||||
HTMLTableRowElement, |
|
||||||
React.HTMLAttributes<HTMLTableRowElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<tr |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableRow.displayName = "TableRow" |
|
||||||
|
|
||||||
const TableHead = React.forwardRef< |
|
||||||
HTMLTableCellElement, |
|
||||||
React.ThHTMLAttributes<HTMLTableCellElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<th |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableHead.displayName = "TableHead" |
|
||||||
|
|
||||||
const TableCell = React.forwardRef< |
|
||||||
HTMLTableCellElement, |
|
||||||
React.TdHTMLAttributes<HTMLTableCellElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<td |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableCell.displayName = "TableCell" |
|
||||||
|
|
||||||
const TableCaption = React.forwardRef< |
|
||||||
HTMLTableCaptionElement, |
|
||||||
React.HTMLAttributes<HTMLTableCaptionElement> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<caption |
|
||||||
ref={ref} |
|
||||||
className={cn("mt-4 text-sm text-muted-foreground", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
TableCaption.displayName = "TableCaption" |
|
||||||
|
|
||||||
export { |
|
||||||
Table, |
|
||||||
TableHeader, |
|
||||||
TableBody, |
|
||||||
TableFooter, |
|
||||||
TableHead, |
|
||||||
TableRow, |
|
||||||
TableCell, |
|
||||||
TableCaption, |
|
||||||
} |
|
@ -1,131 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as ToastPrimitives from "@radix-ui/react-toast" |
|
||||||
import { cva, type VariantProps } from "class-variance-authority" |
|
||||||
import { X } from "lucide-react" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider |
|
||||||
|
|
||||||
const ToastViewport = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Viewport>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Viewport |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName |
|
||||||
|
|
||||||
const toastVariants = cva( |
|
||||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", |
|
||||||
{ |
|
||||||
variants: { |
|
||||||
variant: { |
|
||||||
default: "border bg-background text-foreground", |
|
||||||
destructive: |
|
||||||
"destructive group border-destructive bg-destructive text-destructive-foreground", |
|
||||||
success: |
|
||||||
"green-500 group border-green-500 bg-green-500 text-white", |
|
||||||
}, |
|
||||||
}, |
|
||||||
defaultVariants: { |
|
||||||
variant: "default", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
const Toast = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & |
|
||||||
VariantProps<typeof toastVariants> |
|
||||||
>(({ className, variant, ...props }, ref) => { |
|
||||||
return ( |
|
||||||
<ToastPrimitives.Root |
|
||||||
ref={ref} |
|
||||||
className={cn(toastVariants({ variant }), className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
) |
|
||||||
}) |
|
||||||
Toast.displayName = ToastPrimitives.Root.displayName |
|
||||||
|
|
||||||
const ToastAction = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Action>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Action |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastAction.displayName = ToastPrimitives.Action.displayName |
|
||||||
|
|
||||||
const ToastClose = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Close>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Close |
|
||||||
ref={ref} |
|
||||||
className={cn( |
|
||||||
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", |
|
||||||
className |
|
||||||
)} |
|
||||||
toast-close="" |
|
||||||
{...props} |
|
||||||
> |
|
||||||
<X className="h-4 w-4" /> |
|
||||||
</ToastPrimitives.Close> |
|
||||||
)) |
|
||||||
ToastClose.displayName = ToastPrimitives.Close.displayName |
|
||||||
|
|
||||||
const ToastTitle = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Title>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Title |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm font-semibold [&+div]:text-xs", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastTitle.displayName = ToastPrimitives.Title.displayName |
|
||||||
|
|
||||||
const ToastDescription = React.forwardRef< |
|
||||||
React.ElementRef<typeof ToastPrimitives.Description>, |
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> |
|
||||||
>(({ className, ...props }, ref) => ( |
|
||||||
<ToastPrimitives.Description |
|
||||||
ref={ref} |
|
||||||
className={cn("text-sm opacity-90", className)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
)) |
|
||||||
ToastDescription.displayName = ToastPrimitives.Description.displayName |
|
||||||
|
|
||||||
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> |
|
||||||
|
|
||||||
type ToastActionElement = React.ReactElement<typeof ToastAction> |
|
||||||
|
|
||||||
export { |
|
||||||
type ToastProps, |
|
||||||
type ToastActionElement, |
|
||||||
ToastProvider, |
|
||||||
ToastViewport, |
|
||||||
Toast, |
|
||||||
ToastTitle, |
|
||||||
ToastDescription, |
|
||||||
ToastClose, |
|
||||||
ToastAction, |
|
||||||
} |
|
@ -1,35 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import { useToast } from "@/hooks/use-toast" |
|
||||||
import { |
|
||||||
Toast, |
|
||||||
ToastClose, |
|
||||||
ToastDescription, |
|
||||||
ToastProvider, |
|
||||||
ToastTitle, |
|
||||||
ToastViewport, |
|
||||||
} from "@/components/ui/toast" |
|
||||||
|
|
||||||
export function Toaster() { |
|
||||||
const { toasts } = useToast() |
|
||||||
|
|
||||||
return ( |
|
||||||
<ToastProvider> |
|
||||||
{toasts.map(function ({ id, title, description, action, ...props }) { |
|
||||||
return ( |
|
||||||
<Toast key={id} {...props}> |
|
||||||
<div className="grid gap-1"> |
|
||||||
{title && <ToastTitle>{title}</ToastTitle>} |
|
||||||
{description && ( |
|
||||||
<ToastDescription>{description}</ToastDescription> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
{action} |
|
||||||
<ToastClose /> |
|
||||||
</Toast> |
|
||||||
) |
|
||||||
})} |
|
||||||
<ToastViewport /> |
|
||||||
</ToastProvider> |
|
||||||
) |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
"use client" |
|
||||||
|
|
||||||
import * as React from "react" |
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip" |
|
||||||
|
|
||||||
import { cn } from "@/lib/utils" |
|
||||||
|
|
||||||
const TooltipProvider = TooltipPrimitive.Provider |
|
||||||
|
|
||||||
const Tooltip = TooltipPrimitive.Root |
|
||||||
|
|
||||||
const TooltipTrigger = TooltipPrimitive.Trigger |
|
||||||
|
|
||||||
const TooltipContent = React.forwardRef< |
|
||||||
React.ElementRef<typeof TooltipPrimitive.Content>, |
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> |
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => ( |
|
||||||
<TooltipPrimitive.Portal> |
|
||||||
<TooltipPrimitive.Content |
|
||||||
ref={ref} |
|
||||||
sideOffset={sideOffset} |
|
||||||
className={cn( |
|
||||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", |
|
||||||
className |
|
||||||
)} |
|
||||||
{...props} |
|
||||||
/> |
|
||||||
</TooltipPrimitive.Portal> |
|
||||||
)) |
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName |
|
||||||
|
|
||||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue