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