nice
This commit is contained in:
parent
503172f537
commit
ba5fd9bf85
@ -12,7 +12,7 @@ import {
|
||||
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
@ -334,54 +334,106 @@ const TimePicker = ({
|
||||
onChange,
|
||||
placeholder = 'Choisir une heure',
|
||||
}) => {
|
||||
const [open, setOpen] =
|
||||
useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [
|
||||
hours = '',
|
||||
minutes = '',
|
||||
] = value
|
||||
const [hours = '', minutes = ''] = value
|
||||
? value.split(':')
|
||||
: [];
|
||||
|
||||
const setTimePart = (
|
||||
part,
|
||||
nextValue,
|
||||
const hourOptions = Array.from(
|
||||
{ length: 24 },
|
||||
(_, i) => String(i).padStart(2, '0'),
|
||||
);
|
||||
|
||||
const minuteOptions = Array.from(
|
||||
{ length: 60 },
|
||||
(_, i) => String(i).padStart(2, '0'),
|
||||
);
|
||||
|
||||
const updateTime = (
|
||||
nextHours,
|
||||
nextMinutes,
|
||||
) => {
|
||||
const nextHours =
|
||||
part === 'hours'
|
||||
? nextValue
|
||||
: hours || '00';
|
||||
|
||||
const nextMinutes =
|
||||
part === 'minutes'
|
||||
? nextValue
|
||||
: minutes || '00';
|
||||
|
||||
onChange(
|
||||
`${nextHours}:${nextMinutes}`,
|
||||
`${String(nextHours).padStart(2, '0')}:${String(
|
||||
nextMinutes,
|
||||
).padStart(2, '0')}`,
|
||||
);
|
||||
};
|
||||
|
||||
const hourOptions =
|
||||
Array.from(
|
||||
{ length: 24 },
|
||||
(_, index) =>
|
||||
String(index).padStart(
|
||||
2,
|
||||
'0',
|
||||
),
|
||||
const changeHour = (direction) => {
|
||||
const currentHour = Number(hours || 0);
|
||||
const currentMinute = Number(
|
||||
minutes || 0,
|
||||
);
|
||||
|
||||
const minuteOptions =
|
||||
Array.from(
|
||||
{ length: 60 },
|
||||
(_, index) =>
|
||||
String(index).padStart(
|
||||
2,
|
||||
'0',
|
||||
),
|
||||
let nextHour =
|
||||
currentHour + direction;
|
||||
|
||||
if (nextHour < 0) nextHour = 23;
|
||||
if (nextHour > 23) nextHour = 0;
|
||||
|
||||
updateTime(
|
||||
nextHour,
|
||||
currentMinute,
|
||||
);
|
||||
};
|
||||
|
||||
const changeMinute = (direction) => {
|
||||
const currentHour = Number(hours || 0);
|
||||
const currentMinute = Number(
|
||||
minutes || 0,
|
||||
);
|
||||
|
||||
let nextMinute =
|
||||
currentMinute + direction;
|
||||
|
||||
let nextHour = currentHour;
|
||||
|
||||
if (nextMinute < 0) {
|
||||
nextMinute = 59;
|
||||
nextHour =
|
||||
currentHour === 0
|
||||
? 23
|
||||
: currentHour - 1;
|
||||
}
|
||||
|
||||
if (nextMinute > 59) {
|
||||
nextMinute = 0;
|
||||
nextHour =
|
||||
currentHour === 23
|
||||
? 0
|
||||
: currentHour + 1;
|
||||
}
|
||||
|
||||
updateTime(
|
||||
nextHour,
|
||||
nextMinute,
|
||||
);
|
||||
};
|
||||
|
||||
const handleWheel = (
|
||||
event,
|
||||
type,
|
||||
) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Un événement de molette = une seule unité
|
||||
const direction =
|
||||
event.deltaY > 0 ? 1 : -1;
|
||||
|
||||
if (type === 'hours') {
|
||||
changeHour(direction);
|
||||
} else {
|
||||
changeMinute(direction);
|
||||
}
|
||||
};
|
||||
|
||||
const clearTime = () => {
|
||||
onChange('');
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover
|
||||
@ -402,7 +454,7 @@ const TimePicker = ({
|
||||
<span
|
||||
className={
|
||||
value
|
||||
? ''
|
||||
? 'tabular-nums'
|
||||
: 'text-muted-foreground'
|
||||
}
|
||||
>
|
||||
@ -411,102 +463,106 @@ const TimePicker = ({
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent
|
||||
className="w-[280px] p-3"
|
||||
className="w-[190px] p-3"
|
||||
align="start"
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between border-b pb-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-medium">
|
||||
Choisir une heure
|
||||
</span>
|
||||
|
||||
<span className="rounded-md bg-muted px-2 py-1 text-sm font-medium tabular-nums">
|
||||
<span className="font-mono text-sm font-semibold tabular-nums">
|
||||
{value || '--:--'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="relative flex h-[144px] gap-2 overflow-hidden">
|
||||
{/* Sélection centrale */}
|
||||
<div className="pointer-events-none absolute inset-x-0 top-1/2 z-10 h-9 -translate-y-1/2 rounded-md border bg-muted/50" />
|
||||
|
||||
{/* HEURES */}
|
||||
<div
|
||||
className="flex-1"
|
||||
onWheel={(event) =>
|
||||
handleWheel(
|
||||
event,
|
||||
'hours',
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex h-full flex-col items-center justify-center select-none">
|
||||
<span className="h-9 text-sm text-muted-foreground/40">
|
||||
{hourOptions[
|
||||
(Number(hours || 0) + 23) %
|
||||
24
|
||||
]}
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
||||
Heure
|
||||
</p>
|
||||
<span className="flex h-9 w-full items-center justify-center rounded-md font-mono text-sm font-semibold tabular-nums">
|
||||
{hours || '00'}
|
||||
</span>
|
||||
|
||||
<div className="grid max-h-56 grid-cols-4 gap-1 overflow-y-auto pr-1">
|
||||
{hourOptions.map(
|
||||
(hour) => (
|
||||
<Button
|
||||
key={hour}
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={
|
||||
hours === hour
|
||||
? 'default'
|
||||
: 'ghost'
|
||||
}
|
||||
className="h-8 px-0 tabular-nums"
|
||||
onClick={() =>
|
||||
setTimePart(
|
||||
'hours',
|
||||
hour,
|
||||
)
|
||||
}
|
||||
>
|
||||
{hour}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
<span className="h-9 text-sm text-muted-foreground/40">
|
||||
{hourOptions[
|
||||
(Number(hours || 0) + 1) %
|
||||
24
|
||||
]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* : */}
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="font-bold">
|
||||
:
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* MINUTES */}
|
||||
<div
|
||||
className="flex-1"
|
||||
onWheel={(event) =>
|
||||
handleWheel(
|
||||
event,
|
||||
'minutes',
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex h-full flex-col items-center justify-center select-none">
|
||||
<span className="h-9 text-sm text-muted-foreground/40">
|
||||
{minuteOptions[
|
||||
(Number(minutes || 0) +
|
||||
59) %
|
||||
60
|
||||
]}
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
||||
Minutes
|
||||
</p>
|
||||
<span className="flex h-9 w-full items-center justify-center rounded-md font-mono text-sm font-semibold tabular-nums">
|
||||
{minutes || '00'}
|
||||
</span>
|
||||
|
||||
<div className="grid max-h-56 grid-cols-4 gap-1 overflow-y-auto pr-1">
|
||||
{minuteOptions.map(
|
||||
(minute) => (
|
||||
<Button
|
||||
key={minute}
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={
|
||||
minutes === minute
|
||||
? 'default'
|
||||
: 'ghost'
|
||||
}
|
||||
className="h-8 px-0 tabular-nums"
|
||||
onClick={() =>
|
||||
setTimePart(
|
||||
'minutes',
|
||||
minute,
|
||||
)
|
||||
}
|
||||
>
|
||||
{minute}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
<span className="h-9 text-sm text-muted-foreground/40">
|
||||
{minuteOptions[
|
||||
(Number(minutes || 0) + 1) %
|
||||
60
|
||||
]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="mt-3 border-t pt-3">
|
||||
<p className="mt-2 text-center text-[10px] text-muted-foreground">
|
||||
Molette pour modifier
|
||||
</p>
|
||||
|
||||
<div className="mt-2 border-t pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className="w-full"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-full text-xs"
|
||||
disabled={!value}
|
||||
onClick={() => {
|
||||
onChange('');
|
||||
setOpen(false);
|
||||
}}
|
||||
onClick={clearTime}
|
||||
>
|
||||
Effacer
|
||||
</Button>
|
||||
|
||||
@ -42,16 +42,6 @@ const MOBILE_TIME_COLUMN_WIDTH = 64;
|
||||
const MOBILE_USER_COLUMN_WIDTH = 40;
|
||||
const MOBILE_DEFAULT_CONTENT_WIDTH = 236;
|
||||
|
||||
const USER_COLORS = [
|
||||
'#c56a3d',
|
||||
'#3d7c8c',
|
||||
'#8a5a9e',
|
||||
'#5d8b55',
|
||||
'#c08a32',
|
||||
'#b04f66',
|
||||
'#4772a8',
|
||||
];
|
||||
|
||||
const expandOvernightAttendances = (entries) => {
|
||||
return entries.map((entry) => {
|
||||
if (entry.start_time && !entry.end_time) {
|
||||
@ -241,41 +231,6 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const getUserColor = (user) => {
|
||||
if (user?.color) {
|
||||
return user.color;
|
||||
}
|
||||
|
||||
const id = getUserId(user);
|
||||
|
||||
if (id) {
|
||||
const numericId = Number(id);
|
||||
|
||||
if (!Number.isNaN(numericId)) {
|
||||
return USER_COLORS[
|
||||
numericId % USER_COLORS.length
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const name =
|
||||
user?.first_name ||
|
||||
user?.firstName ||
|
||||
user?.username ||
|
||||
user?.name ||
|
||||
'';
|
||||
|
||||
const hash = [...String(name)].reduce(
|
||||
(total, character) =>
|
||||
total + character.charCodeAt(0),
|
||||
0,
|
||||
);
|
||||
|
||||
return USER_COLORS[
|
||||
hash % USER_COLORS.length
|
||||
];
|
||||
};
|
||||
|
||||
const loadAttendances = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
@ -1214,7 +1169,7 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
).map((user) => ({
|
||||
id: getUserId(user),
|
||||
label: getUserLabel(user),
|
||||
color: getUserColor(user),
|
||||
color: user.color,
|
||||
}));
|
||||
|
||||
return (
|
||||
@ -1599,10 +1554,7 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
user,
|
||||
);
|
||||
|
||||
const color =
|
||||
getUserColor(
|
||||
user,
|
||||
);
|
||||
const color = user.color;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -1720,6 +1672,7 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
style={
|
||||
style
|
||||
}
|
||||
title={`${formatSegmentTime(start)} - ${formatSegmentTime(end)}`}
|
||||
>
|
||||
<div className="p-1 text-xs text-white">
|
||||
{formatSegmentTime(
|
||||
@ -1873,10 +1826,7 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
user,
|
||||
);
|
||||
|
||||
const color =
|
||||
getUserColor(
|
||||
user,
|
||||
);
|
||||
const color = user.color;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -1993,6 +1943,7 @@ const WeeklySummaryPage = ({ userId }) => {
|
||||
style={
|
||||
style
|
||||
}
|
||||
title={`${formatSegmentTime(start)} - ${formatSegmentTime(end)}`}
|
||||
>
|
||||
<div className="p-2 text-xs text-white">
|
||||
<div className="font-medium">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user