@goplusvn/core 0.1.55 → 0.1.56

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.
@@ -1,82 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
-
5
- import { useCrudContext } from "../components/crud-provider";
6
- import { Calendar } from "../../ui/primitives/calendar";
7
- import { Popover, PopoverContent, PopoverTrigger } from "../../ui/primitives/popover";
8
- import { Button } from "../../ui/primitives/button";
9
- import { cn } from "../../utils";
10
- import { format } from "date-fns";
11
- import { Calendar as CalendarIcon } from "lucide-react";
12
- import { FilterConfig } from "../../types";
13
- import { Label } from "../../ui/primitives/label"; // Imported Label
14
- import { DateRangePicker } from "../../ui/forms/date-range-picker"; // Imported DateRangePicker
15
- import { DatePicker } from "../../ui/forms/date-picker"; // Imported DatePicker
16
-
17
- export function DatetimeFilter({ filter, mode: propMode }: { filter: FilterConfig; mode?: string }) {
18
- const { filters, setFilters, addFilter } = useCrudContext(); // Added addFilter
19
- const currentFilter = filters.find((f: any) => f.name === filter.name);
20
- // Since 'mode' is removed, we assume single date selection.
21
- // Wait, the code uses 'mode'.
22
- // "const operator = mode === 'range' ? ..."
23
- // I need to define mode.
24
- const mode = propMode || filter.mode || "single";
25
-
26
- const [value, setValue] = useState<Date | [Date, Date] | null>(
27
- currentFilter?.value ? (currentFilter.value as Date) : null,
28
- );
29
-
30
- useEffect(() => {
31
- const externalFilter = filters.find((f) => f.name === filter.name);
32
- if (externalFilter && externalFilter.value !== value) {
33
- setValue(
34
- mode === "range"
35
- ? (externalFilter.value as [Date, Date])
36
- : (externalFilter.value as Date),
37
- );
38
- }
39
- }, [filters, filter.name, mode, value]);
40
-
41
- const handleChange = (newValue: Date | [Date, Date] | null) => {
42
- setValue(newValue);
43
-
44
- if (!newValue) {
45
- return;
46
- }
47
-
48
- const operator =
49
- mode === "range" ? filter.operator || "between" : filter.operator || "eq";
50
-
51
- addFilter({
52
- name: filter.name,
53
- value: newValue,
54
- operator,
55
- });
56
- };
57
-
58
- return (
59
- <div className="space-y-2">
60
- <Label htmlFor={filter.name}>{filter.label}</Label>
61
- {mode === "range" ? (
62
- <DateRangePicker
63
- value={
64
- value && Array.isArray(value)
65
- ? { from: value[0], to: value[1] }
66
- : undefined
67
- }
68
- onValueChange={(range) => {
69
- handleChange(
70
- range?.from && range?.to ? [range.from, range.to] : null,
71
- );
72
- }}
73
- />
74
- ) : (
75
- <DatePicker
76
- value={(value as Date) || undefined}
77
- onValueChange={(date) => handleChange(date || null)}
78
- />
79
- )}
80
- </div>
81
- );
82
- }
@@ -1,64 +0,0 @@
1
- "use client";
2
-
3
- import type { FilterConfig } from "../../types";
4
-
5
- import { CheckboxFilter } from "./checkbox-filter";
6
- import { DatetimeFilter } from "./datetime-filter";
7
- import { RadioFilter } from "./radio-filter";
8
- import { SelectFilter } from "./select-filter";
9
- import { TextFilter } from "./text-filter";
10
-
11
- interface FilterBuilderProps {
12
- filters: FilterConfig[];
13
- }
14
-
15
- export function FilterBuilder({ filters }: FilterBuilderProps) {
16
- if (!filters || filters.length === 0) {
17
- return null;
18
- }
19
-
20
- return (
21
- <div className="space-y-4">
22
- {filters.map((filter) => {
23
- switch (filter.type) {
24
- case "text":
25
- return <TextFilter key={filter.name} filter={filter} />;
26
- case "datetime":
27
- // Check if filter should be range based on operator
28
- const isRange =
29
- filter.operator === "between" ||
30
- filter.name.toLowerCase().includes("range");
31
- return (
32
- <DatetimeFilter
33
- key={filter.name}
34
- filter={filter}
35
- mode={isRange ? "range" : "single"}
36
- />
37
- );
38
- case "select":
39
- return <SelectFilter key={filter.name} filter={filter} />;
40
- case "checkbox":
41
- return <CheckboxFilter key={filter.name} filter={filter} />;
42
- case "radio":
43
- return <RadioFilter key={filter.name} filter={filter} />;
44
- case "custom":
45
- // Custom filter renderer
46
- if (filter.renderFilter) {
47
- return (
48
- <div key={filter.name}>
49
- {filter.renderFilter({
50
- filter,
51
- name: filter.name,
52
- label: filter.label,
53
- })}
54
- </div>
55
- );
56
- }
57
- return null;
58
- default:
59
- return null;
60
- }
61
- })}
62
- </div>
63
- );
64
- }
@@ -1,78 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { ChevronDown, Filter } from "lucide-react";
5
-
6
- import type { FilterConfig } from "../../types";
7
-
8
- import { Button } from "../../ui";
9
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../ui";
10
- import { useCrudContext } from "../components/crud-provider";
11
- import { FilterBuilder } from "./filter-builder";
12
-
13
- interface CrudFiltersProps {
14
- filters?: FilterConfig[];
15
- }
16
-
17
- export function renderFilterInput(filter: FilterConfig) {
18
- // This function body needs to be implemented based on the actual rendering logic
19
- // For now, returning null or a placeholder
20
- return null;
21
- }
22
-
23
- export function CrudFilters({ filters }: CrudFiltersProps) {
24
- const { filters: activeFilters, clearFilters } = useCrudContext();
25
- const [isOpen, setIsOpen] = useState(false);
26
-
27
- if (!filters || filters.length === 0) {
28
- return null;
29
- }
30
-
31
- const hasActiveFilters = activeFilters.length > 0;
32
-
33
- return (
34
- <Collapsible open={isOpen} onOpenChange={setIsOpen} className="w-full">
35
- <div className="flex items-center justify-between mb-2">
36
- <CollapsibleTrigger asChild>
37
- <Button variant="outline" size="sm" className="gap-2">
38
- <Filter className="h-4 w-4" />
39
- Filters
40
- {hasActiveFilters && (
41
- <span className="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
42
- {activeFilters.length}
43
- </span>
44
- )}
45
- <ChevronDown
46
- className={`h-4 w-4 transition-transform ${
47
- isOpen ? "transform rotate-180" : ""
48
- }`}
49
- />
50
- </Button>
51
- </CollapsibleTrigger>
52
- {hasActiveFilters && (
53
- <Button
54
- variant="ghost"
55
- size="sm"
56
- onClick={clearFilters}
57
- className="h-8"
58
- >
59
- Clear all
60
- </Button>
61
- )}
62
- </div>
63
- <CollapsibleContent className="space-y-4">
64
- <div className="rounded-lg border p-4">
65
- <FilterBuilder filters={filters} />
66
- </div>
67
- </CollapsibleContent>
68
- </Collapsible>
69
- );
70
- }
71
-
72
- // Export all filter components
73
- export { TextFilter } from "./text-filter";
74
- export { DatetimeFilter } from "./datetime-filter";
75
- export { SelectFilter } from "./select-filter";
76
- export { CheckboxFilter } from "./checkbox-filter";
77
- export { RadioFilter } from "./radio-filter";
78
- export { FilterBuilder } from "./filter-builder";
@@ -1,79 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
-
5
- import { dataLoader } from "../lib/data-loader";
6
-
7
- import { useCrudContext } from "../components/crud-provider";
8
- import { RadioGroup, RadioGroupItem } from "../../ui/forms/radio-group";
9
- import { Label } from "../../ui/primitives/label";
10
- import { ScrollArea } from "../../ui/primitives/scroll-area";
11
- import { FilterConfig } from "../../types";
12
-
13
- export function RadioFilter({ filter }: { filter: FilterConfig }) {
14
- const { filters, addFilter, removeFilter } = useCrudContext();
15
- const currentFilter = filters.find((f: any) => f.name === filter.name);
16
- const [value, setValue] = useState<string>(
17
- currentFilter?.value
18
- ? (currentFilter.value as string)
19
- : (filter.defaultValue as string) || "",
20
- );
21
- const [options, setOptions] = useState<
22
- Array<{ label: string; value: string | number | boolean }>
23
- >(filter.options || []);
24
-
25
- // Load options from API if dataSource is provided
26
- useEffect(() => {
27
- if (filter.dataSource) {
28
- dataLoader
29
- .loadOptions(filter.dataSource)
30
- .then(setOptions)
31
- .catch((error) => {
32
- console.error("Error loading filter options:", error);
33
- });
34
- }
35
- }, [filter.dataSource]);
36
-
37
- useEffect(() => {
38
- const externalFilter = filters.find((f) => f.name === filter.name);
39
- if (externalFilter && externalFilter.value !== value) {
40
- setValue((externalFilter.value as string) || "");
41
- }
42
- }, [filters, filter.name, value]);
43
-
44
- const handleChange = (newValue: string) => {
45
- setValue(newValue);
46
-
47
- if (!newValue) {
48
- return;
49
- }
50
-
51
- addFilter({
52
- name: filter.name,
53
- value: newValue,
54
- operator: filter.operator || "eq",
55
- });
56
- };
57
-
58
- return (
59
- <div className="space-y-2">
60
- <Label>{filter.label}</Label>
61
- <RadioGroup value={value} onValueChange={handleChange}>
62
- {options.map((option) => (
63
- <div key={String(option.value)} className="flex items-center space-x-2">
64
- <RadioGroupItem
65
- value={String(option.value)}
66
- id={`${filter.name}-${option.value}`}
67
- />
68
- <Label
69
- htmlFor={`${filter.name}-${option.value}`}
70
- className="font-normal cursor-pointer"
71
- >
72
- {option.label}
73
- </Label>
74
- </div>
75
- ))}
76
- </RadioGroup>
77
- </div>
78
- );
79
- }
@@ -1,148 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
-
5
- import { useCrudContext } from "../components/crud-provider";
6
- import { Label } from "../../ui/primitives/label";
7
- import { FilterConfig } from "../../types";
8
- import {
9
- Select,
10
- SelectContent,
11
- SelectItem,
12
- SelectTrigger,
13
- SelectValue,
14
- } from "../../ui/primitives/select";
15
- import { dataLoader } from "../lib/data-loader";
16
-
17
- export function SelectFilter({ filter }: { filter: FilterConfig }) {
18
- const { filters, addFilter, removeFilter } = useCrudContext();
19
- const currentFilter = filters.find((f: any) => f.name === filter.name);
20
-
21
- // Determine if multiple selection is enabled
22
- const multiple = (filter as any).multiple || (filter.type as string) === "select-multiple";
23
-
24
- const [value, setValue] = useState<string | string[]>(
25
- currentFilter?.value
26
- ? multiple
27
- ? (currentFilter.value as string[])
28
- : (currentFilter.value as string)
29
- : multiple
30
- ? []
31
- : "",
32
- );
33
-
34
- const [options, setOptions] = useState<
35
- Array<{ label: string; value: string | number | boolean }>
36
- >(filter.options || []);
37
-
38
- // Load options from API if dataSource is provided
39
- useEffect(() => {
40
- if (filter.dataSource) {
41
- dataLoader
42
- .loadOptions(filter.dataSource)
43
- .then(setOptions)
44
- .catch((error) => {
45
- console.error("Error loading filter options:", error);
46
- });
47
- }
48
- }, [filter.dataSource]);
49
-
50
- useEffect(() => {
51
- const externalFilter = filters.find((f) => f.name === filter.name);
52
- if (externalFilter && externalFilter.value !== value) {
53
- setValue(
54
- multiple
55
- ? (externalFilter.value as string[])
56
- : (externalFilter.value as string),
57
- );
58
- }
59
- }, [filters, filter.name, multiple, value]);
60
-
61
- const handleChange = (newValue: string | number | undefined) => {
62
- if (newValue === undefined) {
63
- removeFilter(filter.name);
64
- return;
65
- }
66
-
67
- const stringValue = String(newValue);
68
-
69
- if (multiple) {
70
- const currentValues = Array.isArray(value) ? value : [];
71
- const updatedValues = currentValues.includes(stringValue)
72
- ? currentValues.filter((v) => v !== stringValue)
73
- : [...currentValues, stringValue];
74
-
75
- setValue(updatedValues);
76
-
77
- if (updatedValues.length === 0) {
78
- removeFilter(filter.name);
79
- return;
80
- }
81
-
82
- addFilter({
83
- name: filter.name,
84
- value: updatedValues,
85
- operator: filter.operator || "in",
86
- });
87
- } else {
88
- setValue(stringValue);
89
-
90
- if (!stringValue || stringValue === "") {
91
- removeFilter(filter.name);
92
- return;
93
- }
94
-
95
- addFilter({
96
- name: filter.name,
97
- value: stringValue,
98
- operator: filter.operator || "eq",
99
- });
100
- }
101
- };
102
-
103
- if (multiple) {
104
- const selectedValues = Array.isArray(value) ? value : [];
105
- return (
106
- <div className="space-y-2">
107
- <Label>{filter.label}</Label>
108
- <div className="space-y-2 max-h-40 overflow-y-auto border rounded p-2">
109
- {options.map((option) => (
110
- <label
111
- key={String(option.value)}
112
- className="flex items-center space-x-2 cursor-pointer"
113
- >
114
- <input
115
- type="checkbox"
116
- checked={selectedValues.includes(String(option.value))}
117
- onChange={() => handleChange(String(option.value))}
118
- className="rounded border-gray-300"
119
- />
120
- <span>{option.label}</span>
121
- </label>
122
- ))}
123
- </div>
124
- </div>
125
- );
126
- }
127
-
128
- return (
129
- <div className="space-y-2">
130
- <Label htmlFor={filter.name}>{filter.label}</Label>
131
- <Select
132
- value={value as string}
133
- onValueChange={handleChange}
134
- >
135
- <SelectTrigger id={filter.name}>
136
- <SelectValue placeholder={`Select ${filter.label}`} />
137
- </SelectTrigger>
138
- <SelectContent>
139
- {options.map((option) => (
140
- <SelectItem key={String(option.value)} value={String(option.value)}>
141
- {option.label}
142
- </SelectItem>
143
- ))}
144
- </SelectContent>
145
- </Select>
146
- </div>
147
- );
148
- }
@@ -1,81 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useRef, useState } from "react";
4
-
5
- import { useCrudContext } from "../components/crud-provider";
6
- import { Input } from "../../ui/primitives/input";
7
- import { Label } from "../../ui/primitives/label";
8
- import type { FilterConfig } from "../../types";
9
-
10
- export function TextFilter({ filter }: { filter: FilterConfig }) {
11
- const { filters, addFilter, removeFilter } = useCrudContext();
12
- const currentFilter = filters.find((f: any) => f.name === filter.name);
13
- const [value, setValue] = useState<string>(
14
- (currentFilter?.value as string) || (filter.defaultValue as string) || "",
15
- );
16
- const isExternalUpdate = useRef(false);
17
- const debounceMs = 500; // Define debounce delay
18
-
19
- useEffect(() => {
20
- // Sync with context when filters change externally
21
- const externalFilter = filters.find((f) => f.name === filter.name);
22
- const externalValue = (externalFilter?.value as string) || "";
23
- const currentValue = value || "";
24
-
25
- if (externalValue !== currentValue) {
26
- isExternalUpdate.current = true;
27
- setValue(externalValue);
28
- }
29
- }, [filters, filter.name]);
30
-
31
- // Debounced update filter
32
- useEffect(() => {
33
- // Skip debounce if this is an external update
34
- if (isExternalUpdate.current) {
35
- isExternalUpdate.current = false;
36
- return;
37
- }
38
-
39
- const timer = setTimeout(() => {
40
- if (value.trim() === "") {
41
- // Remove filter if empty
42
- removeFilter(filter.name);
43
- } else {
44
- // Add or update filter
45
- addFilter({
46
- name: filter.name,
47
- value: value,
48
- operator: filter.operator || "contains",
49
- });
50
- }
51
- }, debounceMs);
52
-
53
- return () => clearTimeout(timer);
54
- }, [
55
- value,
56
- filter.name,
57
- filter.operator,
58
- debounceMs,
59
- addFilter,
60
- removeFilter,
61
- ]);
62
-
63
- const handleChange = (newValue: string) => {
64
- isExternalUpdate.current = false;
65
- setValue(newValue);
66
- };
67
-
68
- return (
69
- <div className="space-y-2">
70
- <Label htmlFor={filter.name}>{filter.label}</Label>
71
- <Input
72
- id={filter.name}
73
- type="text"
74
- placeholder={filter.label}
75
- value={value}
76
- onChange={(e) => handleChange(e.target.value)}
77
- className="w-full"
78
- />
79
- </div>
80
- );
81
- }