Skip to content

Commit 19b7386

Browse files
committed
La til hurtig datovelger
1 parent a778739 commit 19b7386

File tree

1 file changed

+262
-38
lines changed

1 file changed

+262
-38
lines changed

src/components/chartbuilder/ChartFilters.tsx

+262-38
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ const FILTER_SUGGESTIONS = [
3434
},
3535
];
3636

37+
// Date range suggestions for quick date filtering
38+
const DATE_RANGE_SUGGESTIONS = [
39+
{
40+
id: 'thismonth',
41+
label: 'Denne måneden',
42+
sql: `TIMESTAMP(DATE_TRUNC(CURRENT_DATE(), MONTH))`
43+
},
44+
{
45+
id: 'lastmonth',
46+
label: 'Forrige måned',
47+
sql: `TIMESTAMP(DATE_TRUNC(DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH), MONTH))`
48+
},
49+
{
50+
id: 'thisyear',
51+
label: 'I år',
52+
sql: `TIMESTAMP(DATE_TRUNC(CURRENT_DATE(), YEAR))`
53+
}
54+
];
55+
3756
// Modified interface to receive date range info
3857
interface ChartFiltersProps {
3958
filters: Filter[];
@@ -54,6 +73,8 @@ const ChartFilters = ({
5473
const [customPeriodInputs, setCustomPeriodInputs] = useState<Record<number, {amount: string, unit: string}>>({});
5574
// Change to store single string instead of array
5675
const [appliedSuggestion, setAppliedSuggestion] = useState<string>('');
76+
// Add state for selected date range
77+
const [selectedDateRange, setSelectedDateRange] = useState<string>('');
5778

5879
// Change addFilter to accept a column parameter
5980
const addFilter = (column: string) => {
@@ -62,6 +83,89 @@ const ChartFilters = ({
6283
}
6384
};
6485

86+
// Find the date filter index in the filters array
87+
const getDateFilterIndex = (): number => {
88+
return filters.findIndex(filter => filter.column === 'created_at');
89+
};
90+
91+
// Apply a date range filter
92+
const applyDateRange = (rangeId: string) => {
93+
if (rangeId === 'all') {
94+
// "Hele perioden" option - remove any existing date filters
95+
setSelectedDateRange('all');
96+
const newFilters = filters.filter(f => f.column !== 'created_at');
97+
setFilters(newFilters);
98+
return;
99+
}
100+
101+
if (selectedDateRange === rangeId) {
102+
// Deselect current date range - go back to "Hele perioden"
103+
setSelectedDateRange('all');
104+
105+
// Remove the date filter
106+
const newFilters = filters.filter(f => f.column !== 'created_at');
107+
setFilters(newFilters);
108+
} else {
109+
// Select the new date range
110+
setSelectedDateRange(rangeId);
111+
112+
if (rangeId === 'custom') {
113+
// Special case for custom period
114+
const dateFilterIndex = getDateFilterIndex();
115+
const defaultDays = Math.min(7, maxDaysAvailable);
116+
const sql = `TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -${defaultDays} DAY)`;
117+
118+
if (dateFilterIndex >= 0) {
119+
// Update existing date filter
120+
updateFilter(dateFilterIndex, {
121+
operator: '>=',
122+
value: sql,
123+
dateRangeType: 'custom'
124+
});
125+
setCustomPeriodInputs(prev => ({
126+
...prev,
127+
[dateFilterIndex]: { amount: defaultDays.toString(), unit: 'DAY' }
128+
}));
129+
} else {
130+
// Add new date filter
131+
const newFilter = {
132+
column: 'created_at',
133+
operator: '>=',
134+
value: sql,
135+
dateRangeType: 'custom'
136+
};
137+
setFilters([...filters, newFilter]);
138+
139+
// Initialize custom period inputs for the new filter
140+
// This will be picked up by useEffect
141+
}
142+
} else {
143+
// Regular preset date range
144+
const dateRange = DATE_RANGE_SUGGESTIONS.find(dr => dr.id === rangeId);
145+
if (!dateRange) return;
146+
147+
const dateFilterIndex = getDateFilterIndex();
148+
149+
if (dateFilterIndex >= 0) {
150+
// Update existing date filter
151+
updateFilter(dateFilterIndex, {
152+
operator: '>=',
153+
value: dateRange.sql,
154+
dateRangeType: 'preset'
155+
});
156+
} else {
157+
// Add new date filter
158+
setFilters([...filters, {
159+
column: 'created_at',
160+
operator: '>=',
161+
value: dateRange.sql,
162+
dateRangeType: 'preset'
163+
}]);
164+
}
165+
}
166+
}
167+
};
168+
65169
const removeFilter = (index: number) => {
66170
const filterToRemove = filters[index];
67171
// Check if this filter was added by a suggestion
@@ -76,6 +180,12 @@ const ChartFilters = ({
76180
if (isSuggestionFilter) {
77181
setAppliedSuggestion('');
78182
}
183+
184+
// If removing date filter, clear date range selection
185+
if (filterToRemove.column === 'created_at') {
186+
setSelectedDateRange('');
187+
}
188+
79189
setFilters(filters.filter((_, i) => i !== index));
80190
};
81191

@@ -148,6 +258,15 @@ const ChartFilters = ({
148258
useEffect(() => {
149259
filters.forEach((filter, index) => {
150260
if (filter.column === 'created_at' && !customPeriodInputs[index]) {
261+
// If it's a preset date range, find and select it
262+
if (filter.dateRangeType === 'preset') {
263+
const matchingRange = DATE_RANGE_SUGGESTIONS.find(dr => dr.sql === filter.value);
264+
if (matchingRange) {
265+
setSelectedDateRange(matchingRange.id);
266+
return;
267+
}
268+
}
269+
151270
// Set a sensible default - 7 days or maxDaysAvailable, whichever is smaller
152271
const defaultDays = Math.min(7, maxDaysAvailable);
153272
setCustomPeriodInputs(prev => ({
@@ -159,7 +278,8 @@ const ChartFilters = ({
159278
const sql = `TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -${defaultDays} DAY)`;
160279
updateFilter(index, {
161280
operator: '>=', // Default to >= for date filters
162-
value: sql
281+
value: sql,
282+
dateRangeType: 'custom'
163283
});
164284
}
165285
}
@@ -168,6 +288,9 @@ const ChartFilters = ({
168288

169289
// Update custom period values
170290
const updateCustomPeriod = (index: number, field: 'amount' | 'unit', value: string) => {
291+
// Clear selected date range when using custom period
292+
setSelectedDateRange('');
293+
171294
const currentValues = customPeriodInputs[index] || { amount: '7', unit: 'DAY' };
172295
const newValues = { ...currentValues, [field]: value };
173296
setCustomPeriodInputs({
@@ -186,7 +309,7 @@ const ChartFilters = ({
186309
sql = `TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -${amount * 7} DAY)`;
187310
break;
188311
case 'MONTH':
189-
// Use DATE_ADD and convert to TIMESTAMP for consistent typing
312+
// Use DATE_ADD and convert to TIMESTAMP for consistent typingent typing
190313
sql = `TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL ${amount} MONTH))`;
191314
break;
192315
default:
@@ -195,10 +318,39 @@ const ChartFilters = ({
195318
}
196319

197320
updateFilter(index, {
198-
value: sql
321+
value: sql,
322+
dateRangeType: 'custom'
199323
});
200324
};
201325

326+
// Add helper to check if custom period is active
327+
const isCustomPeriodActive = (): boolean => {
328+
return filters.some(filter =>
329+
filter.column === 'created_at' &&
330+
filter.dateRangeType === 'custom'
331+
);
332+
};
333+
334+
// Convert max days available to a specific date
335+
const getStartDateDisplay = (): string => {
336+
if (!maxDaysAvailable) return 'Velg nettside for å se tilgjengelig data.';
337+
338+
const startDate = new Date();
339+
startDate.setDate(startDate.getDate() - maxDaysAvailable);
340+
341+
// Format date as DD.MM.YYYY (Norwegian format)
342+
const day = String(startDate.getDate()).padStart(2, '0');
343+
const month = String(startDate.getMonth() + 1).padStart(2, '0');
344+
const year = startDate.getFullYear();
345+
346+
return `Data er tilgjengelig fra ${day}.${month}.${year} til i dag.`;
347+
};
348+
349+
// Update to check if any date filter exists
350+
const hasDateFilter = (): boolean => {
351+
return filters.some(filter => filter.column === 'created_at');
352+
};
353+
202354
return (
203355
<section>
204356
<Heading level="2" size="small" spacing>
@@ -240,6 +392,55 @@ const ChartFilters = ({
240392
</div>
241393
</div>
242394

395+
{/* Date Range Quick Picker */}
396+
<div className="mb-4">
397+
<Heading level="3" size="xsmall" spacing>
398+
Datoområde
399+
</Heading>
400+
401+
<div className="flex flex-wrap gap-2 mt-2">
402+
{/* Add "Hele perioden" button as the first option */}
403+
<button
404+
className={`px-3 py-2 rounded-md text-sm border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 ${
405+
!hasDateFilter() || selectedDateRange === 'all'
406+
? 'bg-blue-600 text-white border-blue-700'
407+
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'
408+
}`}
409+
onClick={() => applyDateRange('all')}
410+
>
411+
Alt
412+
</button>
413+
{DATE_RANGE_SUGGESTIONS.map((dateRange) => (
414+
<button
415+
key={dateRange.id}
416+
className={`px-3 py-2 rounded-md text-sm border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 ${
417+
selectedDateRange === dateRange.id
418+
? 'bg-blue-600 text-white border-blue-700'
419+
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'
420+
}`}
421+
onClick={() => applyDateRange(dateRange.id)}
422+
>
423+
{dateRange.label}
424+
</button>
425+
))}
426+
<button
427+
className={`px-3 py-2 rounded-md text-sm border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 ${
428+
isCustomPeriodActive()
429+
? 'bg-blue-600 text-white border-blue-700'
430+
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'
431+
}`}
432+
onClick={() => applyDateRange('custom')}
433+
>
434+
Egendefinert
435+
</button>
436+
</div>
437+
438+
{/* Show info about available date range with actual date */}
439+
<div className="mt-2 text-xs text-gray-600">
440+
{getStartDateDisplay()}
441+
</div>
442+
</div>
443+
243444
{/* Static Filters */}
244445
<div className="mt-6">
245446
<Heading level="3" size="xsmall" spacing>
@@ -249,8 +450,8 @@ const ChartFilters = ({
249450
Statiske filtre er låst til grafen eller tabellen du lager.
250451
</p>
251452

252-
{/* Replace button with dropdown and button combo like in Summarize.tsx */}
253-
<div className="flex gap-2 items-center bg-white p-3 rounded-md border">
453+
{/* Replace button with dropdown and button combo like in Summarize.tsx */}
454+
<div className="flex gap-2 items-center bg-white p-3 rounded-md border">
254455
<Select
255456
label="Legg til filter"
256457
onChange={(e) => {
@@ -371,40 +572,63 @@ const ChartFilters = ({
371572
{/* Simplified Date Input for created_at */}
372573
{filter.column === 'created_at' && (
373574
<div className="mt-3">
374-
<div className="flex items-end gap-2">
375-
<TextField
376-
label="Tid tilbake"
377-
value={customPeriodInputs[index]?.amount || '7'}
378-
onChange={(e) => updateCustomPeriod(index, 'amount', e.target.value)}
379-
type="number"
380-
min="1"
381-
max={(customPeriodInputs[index]?.unit || 'DAY') === 'DAY' ? maxDaysAvailable : undefined}
382-
size="small"
383-
className="w-24"
384-
/>
385-
<Select
386-
label="Tidsenhet"
387-
value={customPeriodInputs[index]?.unit || 'DAY'}
388-
onChange={(e) => updateCustomPeriod(index, 'unit', e.target.value)}
389-
size="small"
390-
>
391-
{TIME_UNITS.map(unit => (
392-
<option key={unit.value} value={unit.value}>
393-
{unit.label}
394-
</option>
395-
))}
396-
</Select>
397-
<div className="text-sm self-center ml-2">
398-
fra nåværende tidspunkt
575+
{filter.dateRangeType === 'preset' ? (
576+
<div className="flex items-center">
577+
<div className="text-sm text-blue-600 font-medium">
578+
Bruker forhåndsdefinert periode: {" "}
579+
{DATE_RANGE_SUGGESTIONS.find(dr => dr.id === selectedDateRange)?.label || 'Egendefinert'}
580+
</div>
581+
<Button
582+
variant="tertiary-neutral"
583+
size="small"
584+
onClick={() => {
585+
// Switch back to custom mode
586+
setSelectedDateRange('');
587+
const defaultDays = Math.min(7, maxDaysAvailable);
588+
const sql = `TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -${defaultDays} DAY)`;
589+
updateFilter(index, {
590+
value: sql,
591+
dateRangeType: 'custom'
592+
});
593+
setCustomPeriodInputs(prev => ({
594+
...prev,
595+
[index]: { amount: defaultDays.toString(), unit: 'DAY' }
596+
}));
597+
}}
598+
className="ml-2"
599+
>
600+
Tilpass
601+
</Button>
399602
</div>
400-
</div>
401-
{/* Add info about available date range */}
402-
<div className="mt-2 text-xs text-gray-600">
403-
{maxDaysAvailable ?
404-
`Data er tilgjengelig for de siste ${maxDaysAvailable} dagene.` :
405-
'Velg nettside for å se tilgjengelig data.'
406-
}
407-
</div>
603+
) : (
604+
<div className="flex items-end gap-2">
605+
<TextField
606+
label="Tid tilbake"
607+
value={customPeriodInputs[index]?.amount || '7'}
608+
onChange={(e) => updateCustomPeriod(index, 'amount', e.target.value)}
609+
type="number"
610+
min="1"
611+
max={(customPeriodInputs[index]?.unit || 'DAY') === 'DAY' ? maxDaysAvailable : undefined}
612+
size="small"
613+
className="w-24"
614+
/>
615+
<Select
616+
label="Tidsenhet"
617+
value={customPeriodInputs[index]?.unit || 'DAY'}
618+
onChange={(e) => updateCustomPeriod(index, 'unit', e.target.value)}
619+
size="small"
620+
>
621+
{TIME_UNITS.map(unit => (
622+
<option key={unit.value} value={unit.value}>
623+
{unit.label}
624+
</option>
625+
))}
626+
</Select>
627+
<div className="text-sm self-center ml-2">
628+
fra nåværende tidspunkt
629+
</div>
630+
</div>
631+
)}
408632
</div>
409633
)}
410634
{/* Event name combobox on its own row */}

0 commit comments

Comments
 (0)