@recruitnepal/shared-packages 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/common/NoData.d.ts +8 -0
- package/dist/components/common/NoData.d.ts.map +1 -0
- package/dist/components/common/NoData.js +4 -0
- package/dist/components/cv/TemplatePicker.d.ts +14 -0
- package/dist/components/cv/TemplatePicker.d.ts.map +1 -0
- package/dist/components/cv/TemplatePicker.js +161 -0
- package/dist/components/cv/TemplateRenderer.d.ts +20 -0
- package/dist/components/cv/TemplateRenderer.d.ts.map +1 -0
- package/dist/components/cv/TemplateRenderer.js +14 -0
- package/dist/components/ui/BulletListTextarea.d.ts +19 -0
- package/dist/components/ui/BulletListTextarea.d.ts.map +1 -0
- package/dist/components/ui/BulletListTextarea.js +60 -0
- package/dist/components/ui/DesignationSelect.d.ts +13 -0
- package/dist/components/ui/DesignationSelect.d.ts.map +1 -0
- package/dist/components/ui/DesignationSelect.js +78 -0
- package/dist/components/ui/IndustrySelect.d.ts +13 -0
- package/dist/components/ui/IndustrySelect.d.ts.map +1 -0
- package/dist/components/ui/IndustrySelect.js +78 -0
- package/dist/components/ui/MultiSelectOptions.d.ts +18 -0
- package/dist/components/ui/MultiSelectOptions.d.ts.map +1 -0
- package/dist/components/ui/MultiSelectOptions.js +85 -0
- package/dist/components/ui/RatingStars.d.ts +10 -0
- package/dist/components/ui/RatingStars.d.ts.map +1 -0
- package/dist/components/ui/RatingStars.js +10 -0
- package/dist/components/ui/command.d.ts +68 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/command.js +24 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +10 -0
- package/dist/hooks/useDebounce.d.ts +2 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.js +9 -0
- package/dist/hooks/useDesignations.d.ts +51 -0
- package/dist/hooks/useDesignations.d.ts.map +1 -0
- package/dist/hooks/useDesignations.js +117 -0
- package/dist/hooks/useIndustries.d.ts +51 -0
- package/dist/hooks/useIndustries.d.ts.map +1 -0
- package/dist/hooks/useIndustries.js +118 -0
- package/dist/hooks/useSkills.d.ts +19 -0
- package/dist/hooks/useSkills.d.ts.map +1 -0
- package/dist/hooks/useSkills.js +39 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -0
- package/dist/stores/cvDrafts.store.d.ts +44 -0
- package/dist/stores/cvDrafts.store.d.ts.map +1 -0
- package/dist/stores/cvDrafts.store.js +86 -0
- package/dist/stores/cvLayout.store.d.ts +24 -0
- package/dist/stores/cvLayout.store.d.ts.map +1 -0
- package/dist/stores/cvLayout.store.js +116 -0
- package/dist/stores/cvPreview.store.d.ts +29 -0
- package/dist/stores/cvPreview.store.d.ts.map +1 -0
- package/dist/stores/cvPreview.store.js +80 -0
- package/dist/types/cv-blocks.types.d.ts +44 -0
- package/dist/types/cv-blocks.types.d.ts.map +1 -0
- package/dist/types/cv-blocks.types.js +1 -0
- package/dist/types/cv-draft.types.d.ts +32 -0
- package/dist/types/cv-draft.types.d.ts.map +1 -0
- package/dist/types/cv-draft.types.js +1 -0
- package/dist/types/template.types.d.ts +122 -0
- package/dist/types/template.types.d.ts.map +1 -0
- package/dist/types/template.types.js +1 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +3 -0
- package/dist/utils/commonDropdownOptions.d.ts +6 -0
- package/dist/utils/commonDropdownOptions.d.ts.map +1 -0
- package/dist/utils/commonDropdownOptions.js +15 -0
- package/dist/utils/cv/pdf/printReset.d.ts +3 -0
- package/dist/utils/cv/pdf/printReset.d.ts.map +1 -0
- package/dist/utils/cv/pdf/printReset.js +41 -0
- package/dist/utils/cv/pdf/styles/base.d.ts +2 -0
- package/dist/utils/cv/pdf/styles/base.d.ts.map +1 -0
- package/dist/utils/cv/pdf/styles/base.js +44 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts +3 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts.map +1 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.js +33 -0
- package/dist/utils/cv-block-converter.d.ts +11 -0
- package/dist/utils/cv-block-converter.d.ts.map +1 -0
- package/dist/utils/cv-block-converter.js +83 -0
- package/dist/utils/generateSearchQuery.d.ts +2 -0
- package/dist/utils/generateSearchQuery.d.ts.map +1 -0
- package/dist/utils/generateSearchQuery.js +5 -0
- package/package.json +51 -44
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Popover, PopoverTrigger, PopoverContent, } from './popover';
|
|
4
|
+
import { Command, CommandInput, CommandItem, CommandList, CommandGroup, CommandEmpty, } from './command';
|
|
5
|
+
import { X } from 'lucide-react';
|
|
6
|
+
import { useEffect, useState, useMemo, useRef } from 'react';
|
|
7
|
+
import { useSkills } from '../../hooks/useSkills';
|
|
8
|
+
// Sub-component for badges
|
|
9
|
+
function SelectedBadges({ skills, removeSkill, }) {
|
|
10
|
+
return (_jsx("div", { className: "flex flex-wrap gap-1 w-full", children: skills.map((skill) => (_jsxs("span", { className: "flex items-center gap-1 px-1.5 py-0.5 rounded-lg bg-primary text-white text-[11px] leading-tight sm:text-xs max-w-full break-words", children: [_jsx("span", { className: "inline-block max-w-[72vw] sm:max-w-full overflow-hidden text-ellipsis", children: skill }), _jsx(X, { className: "w-3 h-3 cursor-pointer shrink-0", onClick: (e) => removeSkill(skill, e) })] }, skill))) }));
|
|
11
|
+
}
|
|
12
|
+
export function MultiSelectOptions({ value, onChange, options, query: externalQuery, disabled, staticOnly = false, token, }) {
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const [search, setSearch] = useState('');
|
|
15
|
+
const [debouncedSearch, setDebouncedSearch] = useState('');
|
|
16
|
+
const loadMoreRef = useRef(null);
|
|
17
|
+
// Debounce search input to reduce API calls
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const timer = setTimeout(() => {
|
|
20
|
+
setDebouncedSearch(search.trim());
|
|
21
|
+
}, 300); // 300ms debounce delay
|
|
22
|
+
return () => clearTimeout(timer);
|
|
23
|
+
}, [search]);
|
|
24
|
+
// Use internal query with search if no external query provided, or if search is active
|
|
25
|
+
// If external query is provided and no search, use it for backward compatibility
|
|
26
|
+
// Skip API query if staticOnly is true
|
|
27
|
+
const shouldUseInternalQuery = !staticOnly && (!externalQuery || debouncedSearch.length > 0);
|
|
28
|
+
const internalSkills = useSkills({
|
|
29
|
+
// Optimized for initial load: small batch, loads more via infinite scroll
|
|
30
|
+
limit: debouncedSearch ? 50 : 20, // 50 when searching, 20 for initial load
|
|
31
|
+
batchSize: 1, // Single page per load for faster response
|
|
32
|
+
queryObject: debouncedSearch ? { search: debouncedSearch } : {},
|
|
33
|
+
token,
|
|
34
|
+
});
|
|
35
|
+
// Use internal query when searching, otherwise use external query if provided
|
|
36
|
+
// If staticOnly, don't use any query
|
|
37
|
+
const query = staticOnly ? undefined : (shouldUseInternalQuery ? internalSkills.skillsQuery : externalQuery);
|
|
38
|
+
// Combine static + API options (no client-side filtering needed - backend handles it)
|
|
39
|
+
// If staticOnly, filter client-side based on search
|
|
40
|
+
const combinedOptions = useMemo(() => {
|
|
41
|
+
if (staticOnly) {
|
|
42
|
+
// Client-side filtering for static options only
|
|
43
|
+
const lowerSearch = debouncedSearch.toLowerCase();
|
|
44
|
+
const filtered = options.filter((opt) => opt.toLowerCase().includes(lowerSearch));
|
|
45
|
+
return filtered;
|
|
46
|
+
}
|
|
47
|
+
// Original behavior: combine static + API options
|
|
48
|
+
const apiOptions = query?.data?.pages.flatMap((page) => page.data?.map((s) => s.skill).filter(Boolean)) || [];
|
|
49
|
+
return Array.from(new Set([...options, ...apiOptions]));
|
|
50
|
+
}, [options, query?.data, staticOnly, debouncedSearch]);
|
|
51
|
+
// Reset search when popover closes
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!open) {
|
|
54
|
+
setSearch('');
|
|
55
|
+
setDebouncedSearch('');
|
|
56
|
+
}
|
|
57
|
+
}, [open]);
|
|
58
|
+
// Infinite scroll using IntersectionObserver
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!loadMoreRef.current || !query?.hasNextPage || query?.isFetchingNextPage)
|
|
61
|
+
return;
|
|
62
|
+
const observer = new IntersectionObserver((entries) => {
|
|
63
|
+
if (entries[0]?.isIntersecting) {
|
|
64
|
+
query.fetchNextPage?.();
|
|
65
|
+
}
|
|
66
|
+
}, { threshold: 0.1 });
|
|
67
|
+
observer.observe(loadMoreRef.current);
|
|
68
|
+
return () => observer.disconnect();
|
|
69
|
+
}, [query?.hasNextPage, query?.isFetchingNextPage, query]);
|
|
70
|
+
// Handlers
|
|
71
|
+
const toggleSkill = (skill) => onChange(value.includes(skill)
|
|
72
|
+
? value.filter((s) => s !== skill)
|
|
73
|
+
: [...value, skill]);
|
|
74
|
+
const removeSkill = (skill, e) => {
|
|
75
|
+
e.stopPropagation();
|
|
76
|
+
onChange(value.filter((s) => s !== skill));
|
|
77
|
+
};
|
|
78
|
+
const trimmedSearch = search.trim();
|
|
79
|
+
// No client-side filtering needed - backend handles search
|
|
80
|
+
// Just check if we can add custom option
|
|
81
|
+
const canAddCustom = trimmedSearch &&
|
|
82
|
+
!combinedOptions.includes(trimmedSearch) &&
|
|
83
|
+
!value.includes(trimmedSearch);
|
|
84
|
+
return (_jsx("div", { className: "w-full", children: _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { disabled: disabled, asChild: true, children: _jsx("button", { type: "button", className: "w-full min-h-10 justify-start border border-slate-200 rounded-md bg-background px-2 py-1 h-auto text-left hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-50", children: value.length > 0 ? (_jsx(SelectedBadges, { skills: value, removeSkill: removeSkill })) : (_jsx("span", { className: "text-muted-foreground", children: "Select Options" })) }) }), _jsx(PopoverContent, { className: "max-w-sm max-h-[300px] overflow-y-auto p-0", children: _jsxs(Command, { shouldFilter: false, children: [_jsx(CommandInput, { value: search, onValueChange: setSearch, placeholder: "Search or add..." }), _jsxs(CommandList, { className: "max-h-[250px] overflow-y-auto", children: [!staticOnly && query?.isFetching && combinedOptions.length === 0 && (_jsx("div", { className: "text-center text-muted-foreground text-xs py-4", children: "Searching..." })), _jsxs(CommandGroup, { children: [combinedOptions.map((skill) => (_jsx(CommandItem, { onSelect: () => toggleSkill(skill), className: "cursor-pointer", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: value.includes(skill), readOnly: true, className: "accent-primary" }), _jsx("span", { children: skill })] }) }, skill))), canAddCustom && (_jsxs(CommandItem, { onSelect: () => toggleSkill(trimmedSearch), className: "cursor-pointer text-primary space-x-2", children: [_jsx("span", { className: "px-2 py-0.5 bg-green-600 hover:bg-green-500 rounded-sm text-xs text-white", children: "Add" }), _jsx("span", { children: trimmedSearch })] }))] }), combinedOptions.length === 0 && !canAddCustom && (!staticOnly && !query?.isFetching) && (_jsx(CommandEmpty, { children: "No matches found." })), !staticOnly && query?.hasNextPage && !query?.isFetching && (_jsx("div", { ref: loadMoreRef, className: "text-center text-muted-foreground text-xs py-2", children: "Loading more..." }))] })] }) })] }) }));
|
|
85
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type RatingStarsProps = {
|
|
3
|
+
value?: number;
|
|
4
|
+
onChange?: (v: number) => void;
|
|
5
|
+
"aria-label"?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare const RatingStars: React.FC<RatingStarsProps>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=RatingStars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RatingStars.d.ts","sourceRoot":"","sources":["../../../src/components/ui/RatingStars.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA6ClD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export const RatingStars = ({ value = 0, onChange, disabled, ...rest }) => {
|
|
3
|
+
return (_jsx("div", { className: "flex items-center gap-1 text-yellow-500", role: "radiogroup", ...rest, children: Array.from({ length: 5 }).map((_, i) => {
|
|
4
|
+
const n = i + 1;
|
|
5
|
+
const filled = n <= (value ?? 0);
|
|
6
|
+
return (_jsx("button", { type: "button", role: "radio", "aria-checked": filled, disabled: disabled, onClick: () => onChange?.(n), className: disabled
|
|
7
|
+
? "opacity-50 cursor-not-allowed h-6 w-6 grid place-items-center rounded"
|
|
8
|
+
: "h-6 w-6 grid place-items-center rounded hover:scale-105", children: _jsx("span", { className: filled ? "text-current" : "", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }) }) }) }, n));
|
|
9
|
+
}) }));
|
|
10
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
declare const Command: React.ForwardRefExoticComponent<Omit<{
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
5
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
6
|
+
} & {
|
|
7
|
+
asChild?: boolean;
|
|
8
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
9
|
+
label?: string;
|
|
10
|
+
shouldFilter?: boolean;
|
|
11
|
+
filter?: (value: string, search: string, keywords?: string[]) => number;
|
|
12
|
+
defaultValue?: string;
|
|
13
|
+
value?: string;
|
|
14
|
+
onValueChange?: (value: string) => void;
|
|
15
|
+
loop?: boolean;
|
|
16
|
+
disablePointerSelection?: boolean;
|
|
17
|
+
vimBindings?: boolean;
|
|
18
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
19
|
+
declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof React.InputHTMLAttributes<HTMLInputElement>> & {
|
|
20
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
21
|
+
} & {
|
|
22
|
+
asChild?: boolean;
|
|
23
|
+
}, "key" | "asChild" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "value" | "onChange"> & {
|
|
24
|
+
value?: string;
|
|
25
|
+
onValueChange?: (search: string) => void;
|
|
26
|
+
} & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
27
|
+
declare const CommandList: React.ForwardRefExoticComponent<Omit<{
|
|
28
|
+
children?: React.ReactNode;
|
|
29
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
30
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
31
|
+
} & {
|
|
32
|
+
asChild?: boolean;
|
|
33
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
34
|
+
label?: string;
|
|
35
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
36
|
+
declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
|
|
37
|
+
children?: React.ReactNode;
|
|
38
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
39
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
40
|
+
} & {
|
|
41
|
+
asChild?: boolean;
|
|
42
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
43
|
+
declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
|
|
44
|
+
children?: React.ReactNode;
|
|
45
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
46
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
47
|
+
} & {
|
|
48
|
+
asChild?: boolean;
|
|
49
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "heading"> & {
|
|
50
|
+
heading?: React.ReactNode;
|
|
51
|
+
value?: string;
|
|
52
|
+
forceMount?: boolean;
|
|
53
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
54
|
+
declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
|
|
55
|
+
children?: React.ReactNode;
|
|
56
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
57
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
58
|
+
} & {
|
|
59
|
+
asChild?: boolean;
|
|
60
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "disabled" | "onSelect"> & {
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
onSelect?: (value: string) => void;
|
|
63
|
+
value?: string;
|
|
64
|
+
keywords?: string[];
|
|
65
|
+
forceMount?: boolean;
|
|
66
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
67
|
+
export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, };
|
|
68
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/components/ui/command.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;sFAYX,CAAC;AAGH,QAAA,MAAM,YAAY;;;;;;;0FAehB,CAAC;AAGH,QAAA,MAAM,WAAW;;;;;;;;sFAef,CAAC;AAGH,QAAA,MAAM,YAAY;;;;;;uJAShB,CAAC;AAGH,QAAA,MAAM,YAAY;;;;;;;;;;sFAYhB,CAAC;AAGH,QAAA,MAAM,WAAW;;;;;;;;;;;;sFAYf,CAAC;AAGH,OAAO,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Command as CommandPrimitive } from 'cmdk';
|
|
5
|
+
import { Search } from 'lucide-react';
|
|
6
|
+
import { cn } from '../../utils/cn';
|
|
7
|
+
const Command = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive, { ref: ref, className: cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', className), ...props })));
|
|
8
|
+
Command.displayName = CommandPrimitive.displayName;
|
|
9
|
+
const CommandInput = React.forwardRef(({ className, ...props }, ref) => (_jsxs("div", { className: "flex items-center border-b px-3", children: [_jsx(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }), _jsx(CommandPrimitive.Input, { ref: ref, className: cn('flex h-11 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 })] })));
|
|
10
|
+
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
|
11
|
+
const CommandList = React.forwardRef(({ className, onWheel, ...props }, ref) => (_jsx(CommandPrimitive.List, { ref: ref, className: cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className), onWheel: (e) => {
|
|
12
|
+
const el = e.currentTarget;
|
|
13
|
+
el.scrollTop += e.deltaY;
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
onWheel?.(e);
|
|
16
|
+
}, ...props })));
|
|
17
|
+
CommandList.displayName = CommandPrimitive.List.displayName;
|
|
18
|
+
const CommandEmpty = React.forwardRef((props, ref) => (_jsx(CommandPrimitive.Empty, { ref: ref, className: "py-6 text-center text-sm", ...props })));
|
|
19
|
+
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
|
20
|
+
const CommandGroup = React.forwardRef(({ className, ...props }, ref) => (_jsx(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 })));
|
|
21
|
+
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
|
22
|
+
const CommandItem = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.Item, { ref: ref, className: cn('relative flex cursor-default 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', className), ...props })));
|
|
23
|
+
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
|
24
|
+
export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
3
|
+
declare const Popover: React.FC<PopoverPrimitive.PopoverProps>;
|
|
4
|
+
declare const PopoverTrigger: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
export { Popover, PopoverTrigger, PopoverContent };
|
|
7
|
+
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../src/components/ui/popover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAG5D,QAAA,MAAM,OAAO,yCAAwB,CAAC;AAEtC,QAAA,MAAM,cAAc,gHAA2B,CAAC;AAEhD,QAAA,MAAM,cAAc,gKAgBlB,CAAC;AAGH,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
5
|
+
import { cn } from '../../utils/cn';
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
8
|
+
const PopoverContent = React.forwardRef(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn('!z-[20050] 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 }) })));
|
|
9
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
10
|
+
export { Popover, PopoverTrigger, PopoverContent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDebounce.d.ts","sourceRoot":"","sources":["../../src/hooks/useDebounce.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAE,MAAY,GAAG,CAAC,CAS/D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export function useDebounce(value, delay = 300) {
|
|
3
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
6
|
+
return () => clearTimeout(handler);
|
|
7
|
+
}, [value, delay]);
|
|
8
|
+
return debouncedValue;
|
|
9
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type Designation = {
|
|
2
|
+
id: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
department?: string;
|
|
6
|
+
level?: string;
|
|
7
|
+
};
|
|
8
|
+
export type DesignationPayload = {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
department?: string;
|
|
12
|
+
level?: string;
|
|
13
|
+
};
|
|
14
|
+
export type UseDesignationsParams = {
|
|
15
|
+
limit?: number;
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
queryObject?: Record<string, unknown>;
|
|
18
|
+
token?: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare const useDesignations: ({ limit, batchSize, queryObject, token, }?: UseDesignationsParams) => {
|
|
21
|
+
designations: Designation[];
|
|
22
|
+
pagination: {
|
|
23
|
+
total: number;
|
|
24
|
+
page: number;
|
|
25
|
+
pages: number;
|
|
26
|
+
} | undefined;
|
|
27
|
+
designationsQuery: import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<{
|
|
28
|
+
data: Designation[];
|
|
29
|
+
nextPage: number | null;
|
|
30
|
+
totalPages: number;
|
|
31
|
+
}, unknown>, Error>;
|
|
32
|
+
isLoading: boolean;
|
|
33
|
+
isFetching: boolean;
|
|
34
|
+
isError: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<import("@tanstack/query-core").InfiniteData<{
|
|
37
|
+
data: Designation[];
|
|
38
|
+
nextPage: number | null;
|
|
39
|
+
totalPages: number;
|
|
40
|
+
}, unknown>, Error>>;
|
|
41
|
+
createDesignation: import("@tanstack/react-query").UseMutationResult<unknown, Error, DesignationPayload, unknown>;
|
|
42
|
+
updateDesignation: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
|
|
43
|
+
id: string;
|
|
44
|
+
data: DesignationPayload;
|
|
45
|
+
}, unknown>;
|
|
46
|
+
deleteDesignation: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
|
|
47
|
+
isCreating: boolean;
|
|
48
|
+
isUpdating: boolean;
|
|
49
|
+
isDeleting: boolean;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=useDesignations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDesignations.d.ts","sourceRoot":"","sources":["../../src/hooks/useDesignations.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAeF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,4CAK7B,qBAA0B;;;;;;;;;;;;;;;;;;;;;;;YAsEc,MAAM;cAAQ,kBAAkB;;;;;;CAgD1E,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useInfiniteQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { apiRequest } from '../utils/api-request';
|
|
4
|
+
import { generateSearchQuery } from '../utils/generateSearchQuery';
|
|
5
|
+
export const useDesignations = ({ limit = 50, batchSize = 1, queryObject = {}, token, } = {}) => {
|
|
6
|
+
const queryClient = useQueryClient();
|
|
7
|
+
const designationsQuery = useInfiniteQuery({
|
|
8
|
+
queryKey: ['designations', limit, batchSize, queryObject],
|
|
9
|
+
initialPageParam: 1,
|
|
10
|
+
enabled: true,
|
|
11
|
+
queryFn: async ({ pageParam = 1 }) => {
|
|
12
|
+
const pagesToFetch = Array.from({ length: batchSize }, (_, i) => pageParam + i);
|
|
13
|
+
const results = await Promise.all(pagesToFetch.map(async (page) => {
|
|
14
|
+
const q = {
|
|
15
|
+
...queryObject,
|
|
16
|
+
page,
|
|
17
|
+
limit,
|
|
18
|
+
};
|
|
19
|
+
if (q.search) {
|
|
20
|
+
q.query = q.search;
|
|
21
|
+
delete q.search;
|
|
22
|
+
}
|
|
23
|
+
const searchQuery = generateSearchQuery(q);
|
|
24
|
+
const res = await apiRequest({
|
|
25
|
+
endpoint: `/designations?${searchQuery}`,
|
|
26
|
+
method: 'GET',
|
|
27
|
+
token: token ?? undefined,
|
|
28
|
+
});
|
|
29
|
+
if (!res.success) {
|
|
30
|
+
const msg = (res.errors?.general?.[0]) || (res.errors && Object.values(res.errors)[0]?.[0]) || 'Failed to fetch designations';
|
|
31
|
+
throw new Error(String(msg));
|
|
32
|
+
}
|
|
33
|
+
const payload = res.data;
|
|
34
|
+
const pag = payload.pagination;
|
|
35
|
+
const pages = pag?.pages ?? 1;
|
|
36
|
+
const nextPage = pag?.nextPage ?? (pag && page < pages ? page + 1 : null);
|
|
37
|
+
return {
|
|
38
|
+
data: payload.data ?? [],
|
|
39
|
+
page,
|
|
40
|
+
pages,
|
|
41
|
+
nextPage,
|
|
42
|
+
};
|
|
43
|
+
}));
|
|
44
|
+
const merged = results.flatMap((r) => r.data);
|
|
45
|
+
const last = results[results.length - 1];
|
|
46
|
+
const nextPage = last.nextPage != null && last.page < last.pages
|
|
47
|
+
? last.nextPage
|
|
48
|
+
: last.page < last.pages
|
|
49
|
+
? last.page + 1
|
|
50
|
+
: null;
|
|
51
|
+
return { data: merged, nextPage, totalPages: last.pages };
|
|
52
|
+
},
|
|
53
|
+
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
|
|
54
|
+
});
|
|
55
|
+
const createDesignation = useMutation({
|
|
56
|
+
mutationFn: async (data) => {
|
|
57
|
+
const res = await apiRequest({
|
|
58
|
+
endpoint: '/designations',
|
|
59
|
+
method: 'POST',
|
|
60
|
+
data,
|
|
61
|
+
token: token ?? undefined,
|
|
62
|
+
});
|
|
63
|
+
if (!res.success)
|
|
64
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to create designation'));
|
|
65
|
+
return res.data;
|
|
66
|
+
},
|
|
67
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['designations'] }),
|
|
68
|
+
});
|
|
69
|
+
const updateDesignation = useMutation({
|
|
70
|
+
mutationFn: async ({ id, data }) => {
|
|
71
|
+
const res = await apiRequest({
|
|
72
|
+
endpoint: `/designations/${id}`,
|
|
73
|
+
method: 'PUT',
|
|
74
|
+
data,
|
|
75
|
+
token: token ?? undefined,
|
|
76
|
+
});
|
|
77
|
+
if (!res.success)
|
|
78
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to update designation'));
|
|
79
|
+
return res.data;
|
|
80
|
+
},
|
|
81
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['designations'] }),
|
|
82
|
+
});
|
|
83
|
+
const deleteDesignation = useMutation({
|
|
84
|
+
mutationFn: async (id) => {
|
|
85
|
+
const res = await apiRequest({
|
|
86
|
+
endpoint: `/designations/${id}`,
|
|
87
|
+
method: 'DELETE',
|
|
88
|
+
token: token ?? undefined,
|
|
89
|
+
});
|
|
90
|
+
if (!res.success)
|
|
91
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to delete designation'));
|
|
92
|
+
return res.data;
|
|
93
|
+
},
|
|
94
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['designations'] }),
|
|
95
|
+
});
|
|
96
|
+
const designations = designationsQuery.data?.pages?.flatMap((p) => p.data) ?? [];
|
|
97
|
+
const lastPage = designationsQuery.data?.pages?.slice(-1)?.[0];
|
|
98
|
+
const pagination = lastPage
|
|
99
|
+
? { total: designations.length, page: 1, pages: lastPage.totalPages ?? 1 }
|
|
100
|
+
: undefined;
|
|
101
|
+
return {
|
|
102
|
+
designations,
|
|
103
|
+
pagination,
|
|
104
|
+
designationsQuery,
|
|
105
|
+
isLoading: designationsQuery.isLoading,
|
|
106
|
+
isFetching: designationsQuery.isFetching,
|
|
107
|
+
isError: designationsQuery.isError,
|
|
108
|
+
error: designationsQuery.error,
|
|
109
|
+
refetch: designationsQuery.refetch,
|
|
110
|
+
createDesignation,
|
|
111
|
+
updateDesignation,
|
|
112
|
+
deleteDesignation,
|
|
113
|
+
isCreating: createDesignation.isPending,
|
|
114
|
+
isUpdating: updateDesignation.isPending,
|
|
115
|
+
isDeleting: deleteDesignation.isPending,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type Industry = {
|
|
2
|
+
id: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
category?: string;
|
|
6
|
+
status?: string;
|
|
7
|
+
};
|
|
8
|
+
export type IndustryPayload = {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
category?: string;
|
|
12
|
+
status?: string;
|
|
13
|
+
};
|
|
14
|
+
export type UseIndustriesParams = {
|
|
15
|
+
limit?: number;
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
queryObject?: Record<string, unknown>;
|
|
18
|
+
token?: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare const useIndustries: (params?: UseIndustriesParams) => {
|
|
21
|
+
industries: Industry[];
|
|
22
|
+
pagination: {
|
|
23
|
+
total: number;
|
|
24
|
+
page: number;
|
|
25
|
+
pages: number;
|
|
26
|
+
} | undefined;
|
|
27
|
+
industriesQuery: import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<{
|
|
28
|
+
data: Industry[];
|
|
29
|
+
nextPage: number | null;
|
|
30
|
+
totalPages: number;
|
|
31
|
+
}, unknown>, Error>;
|
|
32
|
+
isLoading: boolean;
|
|
33
|
+
isFetching: boolean;
|
|
34
|
+
isError: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<import("@tanstack/query-core").InfiniteData<{
|
|
37
|
+
data: Industry[];
|
|
38
|
+
nextPage: number | null;
|
|
39
|
+
totalPages: number;
|
|
40
|
+
}, unknown>, Error>>;
|
|
41
|
+
createIndustry: import("@tanstack/react-query").UseMutationResult<unknown, Error, IndustryPayload, unknown>;
|
|
42
|
+
updateIndustry: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
|
|
43
|
+
id: string;
|
|
44
|
+
data: IndustryPayload;
|
|
45
|
+
}, unknown>;
|
|
46
|
+
deleteIndustry: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
|
|
47
|
+
isCreating: boolean;
|
|
48
|
+
isUpdating: boolean;
|
|
49
|
+
isDeleting: boolean;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=useIndustries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIndustries.d.ts","sourceRoot":"","sources":["../../src/hooks/useIndustries.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAeF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAQ,mBAAwB;;;;;;;;;;;;;;;;;;;;;;;YAuEnB,MAAM;cAAQ,eAAe;;;;;;CAgDvE,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useInfiniteQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { apiRequest } from '../utils/api-request';
|
|
4
|
+
import { generateSearchQuery } from '../utils/generateSearchQuery';
|
|
5
|
+
export const useIndustries = (params = {}) => {
|
|
6
|
+
const { limit = 50, batchSize = 1, queryObject = {}, token } = params;
|
|
7
|
+
const queryClient = useQueryClient();
|
|
8
|
+
const industriesQuery = useInfiniteQuery({
|
|
9
|
+
queryKey: ['industries', limit, batchSize, queryObject],
|
|
10
|
+
initialPageParam: 1,
|
|
11
|
+
enabled: true,
|
|
12
|
+
queryFn: async ({ pageParam = 1 }) => {
|
|
13
|
+
const pagesToFetch = Array.from({ length: batchSize }, (_, i) => pageParam + i);
|
|
14
|
+
const results = await Promise.all(pagesToFetch.map(async (page) => {
|
|
15
|
+
const q = {
|
|
16
|
+
...queryObject,
|
|
17
|
+
page,
|
|
18
|
+
limit,
|
|
19
|
+
};
|
|
20
|
+
if (q.search) {
|
|
21
|
+
q.query = q.search;
|
|
22
|
+
delete q.search;
|
|
23
|
+
}
|
|
24
|
+
const searchQuery = generateSearchQuery(q);
|
|
25
|
+
const res = await apiRequest({
|
|
26
|
+
endpoint: `/industries?${searchQuery}`,
|
|
27
|
+
method: 'GET',
|
|
28
|
+
token: token ?? undefined,
|
|
29
|
+
});
|
|
30
|
+
if (!res.success) {
|
|
31
|
+
const msg = (res.errors?.general?.[0]) || (res.errors && Object.values(res.errors)[0]?.[0]) || 'Failed to fetch industries';
|
|
32
|
+
throw new Error(String(msg));
|
|
33
|
+
}
|
|
34
|
+
const payload = res.data;
|
|
35
|
+
const pag = payload.pagination;
|
|
36
|
+
const pages = pag?.pages ?? 1;
|
|
37
|
+
const nextPage = pag?.nextPage ?? (pag && page < pages ? page + 1 : null);
|
|
38
|
+
return {
|
|
39
|
+
data: payload.data ?? [],
|
|
40
|
+
page,
|
|
41
|
+
pages,
|
|
42
|
+
nextPage,
|
|
43
|
+
};
|
|
44
|
+
}));
|
|
45
|
+
const merged = results.flatMap((r) => r.data);
|
|
46
|
+
const last = results[results.length - 1];
|
|
47
|
+
const nextPage = last.nextPage != null && last.page < last.pages
|
|
48
|
+
? last.nextPage
|
|
49
|
+
: last.page < last.pages
|
|
50
|
+
? last.page + 1
|
|
51
|
+
: null;
|
|
52
|
+
return { data: merged, nextPage, totalPages: last.pages };
|
|
53
|
+
},
|
|
54
|
+
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
|
|
55
|
+
});
|
|
56
|
+
const createIndustry = useMutation({
|
|
57
|
+
mutationFn: async (data) => {
|
|
58
|
+
const res = await apiRequest({
|
|
59
|
+
endpoint: '/industries',
|
|
60
|
+
method: 'POST',
|
|
61
|
+
data,
|
|
62
|
+
token: token ?? undefined,
|
|
63
|
+
});
|
|
64
|
+
if (!res.success)
|
|
65
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to create industry'));
|
|
66
|
+
return res.data;
|
|
67
|
+
},
|
|
68
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['industries'] }),
|
|
69
|
+
});
|
|
70
|
+
const updateIndustry = useMutation({
|
|
71
|
+
mutationFn: async ({ id, data }) => {
|
|
72
|
+
const res = await apiRequest({
|
|
73
|
+
endpoint: `/industries/${id}`,
|
|
74
|
+
method: 'PUT',
|
|
75
|
+
data,
|
|
76
|
+
token: token ?? undefined,
|
|
77
|
+
});
|
|
78
|
+
if (!res.success)
|
|
79
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to update industry'));
|
|
80
|
+
return res.data;
|
|
81
|
+
},
|
|
82
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['industries'] }),
|
|
83
|
+
});
|
|
84
|
+
const deleteIndustry = useMutation({
|
|
85
|
+
mutationFn: async (id) => {
|
|
86
|
+
const res = await apiRequest({
|
|
87
|
+
endpoint: `/industries/${id}`,
|
|
88
|
+
method: 'DELETE',
|
|
89
|
+
token: token ?? undefined,
|
|
90
|
+
});
|
|
91
|
+
if (!res.success)
|
|
92
|
+
throw new Error(String((res.errors?.general?.[0]) || 'Failed to delete industry'));
|
|
93
|
+
return res.data;
|
|
94
|
+
},
|
|
95
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['industries'] }),
|
|
96
|
+
});
|
|
97
|
+
const industries = industriesQuery.data?.pages?.flatMap((p) => p.data) ?? [];
|
|
98
|
+
const lastPage = industriesQuery.data?.pages?.slice(-1)?.[0];
|
|
99
|
+
const pagination = lastPage
|
|
100
|
+
? { total: industries.length, page: 1, pages: lastPage.totalPages ?? 1 }
|
|
101
|
+
: undefined;
|
|
102
|
+
return {
|
|
103
|
+
industries,
|
|
104
|
+
pagination,
|
|
105
|
+
industriesQuery,
|
|
106
|
+
isLoading: industriesQuery.isLoading,
|
|
107
|
+
isFetching: industriesQuery.isFetching,
|
|
108
|
+
isError: industriesQuery.isError,
|
|
109
|
+
error: industriesQuery.error,
|
|
110
|
+
refetch: industriesQuery.refetch,
|
|
111
|
+
createIndustry,
|
|
112
|
+
updateIndustry,
|
|
113
|
+
deleteIndustry,
|
|
114
|
+
isCreating: createIndustry.isPending,
|
|
115
|
+
isUpdating: updateIndustry.isPending,
|
|
116
|
+
isDeleting: deleteIndustry.isPending,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Skill = {
|
|
2
|
+
id: string;
|
|
3
|
+
skill: string;
|
|
4
|
+
};
|
|
5
|
+
export type UseSkillsParams = {
|
|
6
|
+
limit?: number;
|
|
7
|
+
batchSize?: number;
|
|
8
|
+
queryObject?: Record<string, unknown>;
|
|
9
|
+
/** Optional auth token (e.g. from useSessionStore) */
|
|
10
|
+
token?: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare const useSkills: ({ limit, batchSize, queryObject, token, }?: UseSkillsParams) => {
|
|
13
|
+
skillsQuery: import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<{
|
|
14
|
+
data: Skill[];
|
|
15
|
+
nextPage: number | null;
|
|
16
|
+
totalPages: number;
|
|
17
|
+
}, unknown>, Error>;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=useSkills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSkills.d.ts","sourceRoot":"","sources":["../../src/hooks/useSkills.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAYF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,4CAKvB,eAAoB;;;;;;CAqCtB,CAAC"}
|