feat: introduce Teams & OKRs feature with models, types, and UI components for team management and objective tracking
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 12m53s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 12m53s
This commit is contained in:
71
src/components/ui/Select.tsx
Normal file
71
src/components/ui/Select.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { forwardRef, SelectHTMLAttributes } from 'react';
|
||||
|
||||
interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
|
||||
label?: string;
|
||||
error?: string;
|
||||
options: SelectOption[];
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||
({ className = '', label, error, id, options, placeholder, ...props }, ref) => {
|
||||
const selectId = id || props.name;
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{label && (
|
||||
<label htmlFor={selectId} className="mb-2 block text-sm font-medium text-foreground">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className="relative">
|
||||
<select
|
||||
ref={ref}
|
||||
id={selectId}
|
||||
className={`
|
||||
w-full appearance-none rounded-lg border bg-input px-4 py-2.5 pr-10 text-foreground
|
||||
placeholder:text-muted-foreground
|
||||
focus:outline-none focus:ring-2 focus:ring-primary/20
|
||||
disabled:cursor-not-allowed disabled:opacity-50
|
||||
${error ? 'border-destructive focus:border-destructive' : 'border-input-border focus:border-primary'}
|
||||
${className}
|
||||
`}
|
||||
{...props}
|
||||
>
|
||||
{placeholder && (
|
||||
<option value="" disabled={props.required}>
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value} disabled={option.disabled}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{/* Custom arrow icon */}
|
||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||
<svg
|
||||
className="h-5 w-5 text-muted-foreground"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
{error && <p className="mt-1.5 text-sm text-destructive">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Select.displayName = 'Select';
|
||||
|
||||
46
src/components/ui/ToggleGroup.tsx
Normal file
46
src/components/ui/ToggleGroup.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface ToggleOption<T extends string> {
|
||||
value: T;
|
||||
label: string;
|
||||
icon?: ReactNode;
|
||||
}
|
||||
|
||||
interface ToggleGroupProps<T extends string> {
|
||||
value: T;
|
||||
options: ToggleOption<T>[];
|
||||
onChange: (value: T) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ToggleGroup<T extends string>({
|
||||
value,
|
||||
options,
|
||||
onChange,
|
||||
className = '',
|
||||
}: ToggleGroupProps<T>) {
|
||||
return (
|
||||
<div className={`flex items-center gap-2 rounded-lg border border-border bg-card p-1 ${className}`}>
|
||||
{options.map((option) => (
|
||||
<button
|
||||
key={option.value}
|
||||
type="button"
|
||||
onClick={() => onChange(option.value)}
|
||||
className={`
|
||||
flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors
|
||||
${value === option.value
|
||||
? 'bg-[#8b5cf6] text-white shadow-sm'
|
||||
: 'text-muted hover:text-foreground hover:bg-card-hover'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{option.icon && <span className="flex items-center">{option.icon}</span>}
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,4 +9,7 @@ export { EditableMotivatorTitle } from './EditableMotivatorTitle';
|
||||
export { EditableYearReviewTitle } from './EditableYearReviewTitle';
|
||||
export { Input } from './Input';
|
||||
export { Modal, ModalFooter } from './Modal';
|
||||
export { Select } from './Select';
|
||||
export { Textarea } from './Textarea';
|
||||
export { ToggleGroup } from './ToggleGroup';
|
||||
export type { ToggleOption } from './ToggleGroup';
|
||||
|
||||
Reference in New Issue
Block a user