Segurança
@@ -167,3 +169,131 @@ function SettingsPage() {
);
}
+
+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 = {};
+ 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 (
+
+
+
+
Fuso horário
+
+
+ Usado para agendar e exibir suas automações (cronjobs).
+
+
+
+
+
+
+
+
+
+
+ Agora neste fuso: {nowInTz}
+
+ {detected && detected !== value && (
+
+ Seu navegador está em {detected}.
+ {isDetectedListed && (
+
+ )}
+
+ )}
+
+
+
+
+
+ );
+}