feat: creats frontend for the project

This commit is contained in:
vakabunga
2026-03-02 00:33:09 +03:00
parent 4d67636633
commit cd56e2bf9d
37 changed files with 3762 additions and 0 deletions

View 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)}>
&larr;
</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">&mdash;</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)}>
&rarr;
</button>
)}
</div>
</div>
);
}