feat: creats frontend for the project
This commit is contained in:
134
frontend/src/components/PeriodSelector.tsx
Normal file
134
frontend/src/components/PeriodSelector.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { toISODate } from '../utils/format';
|
||||
|
||||
export type PeriodMode = 'week' | 'month' | 'year' | 'custom';
|
||||
|
||||
export interface PeriodState {
|
||||
mode: PeriodMode;
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
period: PeriodState;
|
||||
onChange: (period: PeriodState) => void;
|
||||
}
|
||||
|
||||
const MODE_LABELS: Record<PeriodMode, string> = {
|
||||
week: 'Неделя',
|
||||
month: 'Месяц',
|
||||
year: 'Год',
|
||||
custom: 'Период',
|
||||
};
|
||||
|
||||
export function PeriodSelector({ period, onChange }: Props) {
|
||||
const setMode = (mode: PeriodMode) => {
|
||||
const now = new Date();
|
||||
let from: Date;
|
||||
switch (mode) {
|
||||
case 'week': {
|
||||
const day = now.getDay();
|
||||
const diff = day === 0 ? 6 : day - 1;
|
||||
from = new Date(now);
|
||||
from.setDate(now.getDate() - diff);
|
||||
break;
|
||||
}
|
||||
case 'month':
|
||||
from = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
break;
|
||||
case 'year':
|
||||
from = new Date(now.getFullYear(), 0, 1);
|
||||
break;
|
||||
case 'custom':
|
||||
onChange({ mode, from: period.from, to: period.to });
|
||||
return;
|
||||
}
|
||||
onChange({ mode, from: toISODate(from), to: toISODate(now) });
|
||||
};
|
||||
|
||||
const navigate = (direction: -1 | 1) => {
|
||||
const fromDate = new Date(period.from);
|
||||
let newFrom: Date;
|
||||
let newTo: Date;
|
||||
|
||||
switch (period.mode) {
|
||||
case 'week':
|
||||
newFrom = new Date(fromDate);
|
||||
newFrom.setDate(fromDate.getDate() + 7 * direction);
|
||||
newTo = new Date(newFrom);
|
||||
newTo.setDate(newFrom.getDate() + 6);
|
||||
break;
|
||||
case 'month':
|
||||
newFrom = new Date(
|
||||
fromDate.getFullYear(),
|
||||
fromDate.getMonth() + direction,
|
||||
1,
|
||||
);
|
||||
newTo = new Date(
|
||||
newFrom.getFullYear(),
|
||||
newFrom.getMonth() + 1,
|
||||
0,
|
||||
);
|
||||
break;
|
||||
case 'year':
|
||||
newFrom = new Date(fromDate.getFullYear() + direction, 0, 1);
|
||||
newTo = new Date(newFrom.getFullYear(), 11, 31);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
onChange({
|
||||
mode: period.mode,
|
||||
from: toISODate(newFrom),
|
||||
to: toISODate(newTo),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="period-selector">
|
||||
<div className="period-modes">
|
||||
{(['week', 'month', 'year', 'custom'] as PeriodMode[]).map(
|
||||
(m) => (
|
||||
<button
|
||||
key={m}
|
||||
className={`btn-preset ${period.mode === m ? 'active' : ''}`}
|
||||
onClick={() => setMode(m)}
|
||||
>
|
||||
{MODE_LABELS[m]}
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="period-nav">
|
||||
{period.mode !== 'custom' && (
|
||||
<button className="btn-page" onClick={() => navigate(-1)}>
|
||||
←
|
||||
</button>
|
||||
)}
|
||||
<div className="period-dates">
|
||||
<input
|
||||
type="date"
|
||||
value={period.from}
|
||||
onChange={(e) =>
|
||||
onChange({ ...period, mode: 'custom', from: e.target.value })
|
||||
}
|
||||
/>
|
||||
<span className="filter-separator">—</span>
|
||||
<input
|
||||
type="date"
|
||||
value={period.to}
|
||||
onChange={(e) =>
|
||||
onChange({ ...period, mode: 'custom', to: e.target.value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{period.mode !== 'custom' && (
|
||||
<button className="btn-page" onClick={() => navigate(1)}>
|
||||
→
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user