mirror of
https://github.com/domfelipe/mika-agent-assist.git
synced 2026-08-07 08:36:44 +00:00
Changes
Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
This commit is contained in:
parent
136f1b98cd
commit
badb1978f2
1 changed files with 130 additions and 0 deletions
|
|
@ -154,6 +154,8 @@ function SettingsPage() {
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<TimezoneSection currentTimezone={profile?.timezone ?? "America/Sao_Paulo"} userId={user?.id} />
|
||||||
|
|
||||||
<section className="rounded-xl border border-border bg-card p-6 shadow-soft">
|
<section className="rounded-xl border border-border bg-card p-6 shadow-soft">
|
||||||
<h2 className="text-lg font-semibold mb-1">Segurança</h2>
|
<h2 className="text-lg font-semibold mb-1">Segurança</h2>
|
||||||
<p className="text-sm text-muted-foreground mb-6">
|
<p className="text-sm text-muted-foreground mb-6">
|
||||||
|
|
@ -167,3 +169,131 @@ function SettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TimezoneSection({
|
||||||
|
currentTimezone,
|
||||||
|
userId,
|
||||||
|
}: {
|
||||||
|
currentTimezone: string;
|
||||||
|
userId: string | undefined;
|
||||||
|
}) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [value, setValue] = useState(currentTimezone);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(currentTimezone);
|
||||||
|
}, [currentTimezone]);
|
||||||
|
|
||||||
|
const grouped = useMemo(() => {
|
||||||
|
const groups: Record<string, typeof TIMEZONE_OPTIONS> = {};
|
||||||
|
for (const tz of TIMEZONE_OPTIONS) {
|
||||||
|
(groups[tz.group] ??= []).push(tz);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const detected = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const nowInTz = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return new Intl.DateTimeFormat("pt-BR", {
|
||||||
|
timeZone: value,
|
||||||
|
dateStyle: "short",
|
||||||
|
timeStyle: "medium",
|
||||||
|
}).format(new Date());
|
||||||
|
} catch {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const update = useMutation({
|
||||||
|
mutationFn: async (tz: string) => {
|
||||||
|
if (!userId) throw new Error("Sem sessão");
|
||||||
|
const { error } = await supabase
|
||||||
|
.from("profiles")
|
||||||
|
.update({ timezone: tz })
|
||||||
|
.eq("id", userId);
|
||||||
|
if (error) throw error;
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Fuso horário atualizado.");
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["profile"] });
|
||||||
|
},
|
||||||
|
onError: (e: Error) => toast.error(translateAuthError(e.message)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const dirty = value !== currentTimezone;
|
||||||
|
const isDetectedListed = detected
|
||||||
|
? TIMEZONE_OPTIONS.some((t) => t.value === detected)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="rounded-xl border border-border bg-card p-6 shadow-soft">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<Globe className="h-5 w-5 text-primary" />
|
||||||
|
<h2 className="text-lg font-semibold">Fuso horário</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground mb-6">
|
||||||
|
Usado para agendar e exibir suas automações (cronjobs).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label htmlFor="timezone">Selecione o fuso</Label>
|
||||||
|
<Select value={value} onValueChange={setValue}>
|
||||||
|
<SelectTrigger id="timezone" className="rounded-lg">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="max-h-80">
|
||||||
|
{Object.entries(grouped).map(([group, items]) => (
|
||||||
|
<SelectGroup key={group}>
|
||||||
|
<SelectLabel>{group}</SelectLabel>
|
||||||
|
{items.map((tz) => (
|
||||||
|
<SelectItem key={tz.value} value={tz.value}>
|
||||||
|
{tz.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-xs text-muted-foreground space-y-1">
|
||||||
|
<p>
|
||||||
|
<span className="font-medium text-foreground">Agora neste fuso:</span> {nowInTz}
|
||||||
|
</p>
|
||||||
|
{detected && detected !== value && (
|
||||||
|
<p>
|
||||||
|
Seu navegador está em <span className="font-mono">{detected}</span>.
|
||||||
|
{isDetectedListed && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ml-1 text-primary underline"
|
||||||
|
onClick={() => setValue(detected)}
|
||||||
|
>
|
||||||
|
Usar este fuso
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => update.mutate(value)}
|
||||||
|
disabled={!dirty || update.isPending}
|
||||||
|
className="rounded-lg bg-primary hover:bg-primary-dark text-primary-foreground transition-all duration-150 active:scale-[0.98]"
|
||||||
|
>
|
||||||
|
{update.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||||
|
Salvar fuso horário
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue