Finally working timezones
This commit is contained in:
parent
c925ae8811
commit
aaf351d328
@ -1,26 +0,0 @@
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
|
||||
export const resetCmd = new Command()
|
||||
.name("reset")
|
||||
.description("Initializes the database with default data")
|
||||
.action(async () => {
|
||||
await getAPIClient()
|
||||
.users.create.mutate({
|
||||
email: "ryan@ryanpandya.com",
|
||||
name: "Ryan Pandya",
|
||||
password: "pleasework",
|
||||
confirmPassword: "pleasework",
|
||||
})
|
||||
.then(printObject)
|
||||
.catch(
|
||||
printError(
|
||||
`Unable to create user for Ryan`,
|
||||
),
|
||||
);
|
||||
});
|
||||
36
apps/cli/src/commands/settings.ts
Normal file
36
apps/cli/src/commands/settings.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
|
||||
export const settingsCmd = new Command()
|
||||
.name("settings")
|
||||
.description("Manipulate user settings");
|
||||
|
||||
settingsCmd
|
||||
.command("timezone")
|
||||
.description("get or update timezone")
|
||||
.argument("[timezone]", "the timezone to set")
|
||||
.action(async (timezone?) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
if (timezone) {
|
||||
const res = await api.users.changeTimezone.mutate({ newTimezone: timezone });
|
||||
console.log(`Updated to ${res}.`)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const res = await api.users.getTimezone.query();
|
||||
console.log(`Current timezone is ${res}.`);
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed to do the thing", error as object);
|
||||
}
|
||||
});
|
||||
@ -6,6 +6,7 @@ import { colorsCmd } from "@/commands/colors";
|
||||
import { categoriesCmd } from "@/commands/categories";
|
||||
import { daysCmd } from "@/commands/days";
|
||||
import { hoursCmd } from "./commands/hours";
|
||||
import { settingsCmd } from "./commands/settings";
|
||||
|
||||
import { config } from "dotenv";
|
||||
|
||||
@ -42,6 +43,7 @@ program.addCommand(daysCmd);
|
||||
program.addCommand(colorsCmd);
|
||||
program.addCommand(categoriesCmd);
|
||||
program.addCommand(hoursCmd);
|
||||
program.addCommand(settingsCmd);
|
||||
|
||||
|
||||
setGlobalOptions(program.opts());
|
||||
|
||||
@ -10,6 +10,7 @@ export default async function DayPage({
|
||||
params: { dateQuery: string };
|
||||
}) {
|
||||
let day;
|
||||
|
||||
try {
|
||||
day = await api.days.get({ dateQuery: params.dateQuery });
|
||||
} catch (e) {
|
||||
|
||||
9
apps/web/app/settings/app/page.tsx
Normal file
9
apps/web/app/settings/app/page.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import AppSettings from "@/components/settings/AppSettings";
|
||||
|
||||
export default async function AppSettingsPage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<AppSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -23,8 +23,6 @@ export default async function DayView({
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
// const entries = await api.entries.get({ day: day });
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
|
||||
@ -32,9 +30,11 @@ export default async function DayView({
|
||||
<span className="text-2xl">
|
||||
{format(day.date, "EEEE, MMMM do")}
|
||||
</span>
|
||||
<div><MoodStars
|
||||
day={day}
|
||||
/></div>
|
||||
<div>
|
||||
<MoodStars
|
||||
day={day}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
@ -45,7 +45,7 @@ export default async function DayView({
|
||||
|
||||
<Separator />
|
||||
|
||||
{day.hours}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -4,19 +4,19 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { api } from "@/server/api/client";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
import { Archive, Home, Search, Tag } from "lucide-react";
|
||||
|
||||
import serverConfig from "@lifetracker/shared/config";
|
||||
|
||||
import AllLists from "./AllLists";
|
||||
import TimezoneDisplay from "./TimezoneDisplay";
|
||||
|
||||
export default async function Sidebar() {
|
||||
|
||||
|
||||
const session = await getServerAuthSession();
|
||||
if (!session) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
const lists = await api.users.list();
|
||||
|
||||
const searchItem = serverConfig.meilisearch
|
||||
? [
|
||||
{
|
||||
@ -61,7 +61,7 @@ export default async function Sidebar() {
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="mt-auto flex items-center border-t pt-2 text-sm text-gray-400">
|
||||
Lifetracker v{serverConfig.serverVersion}
|
||||
<TimezoneDisplay />
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
57
apps/web/components/dashboard/sidebar/TimezoneDisplay.tsx
Normal file
57
apps/web/components/dashboard/sidebar/TimezoneDisplay.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
import { useState, useEffect, use, } from "react";
|
||||
import Link from "next/link";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { timezones } from '@/lib/timezones';
|
||||
import { useTimezone } from "@lifetracker/shared-react/hooks/timezones";
|
||||
import LoadingSpinner from "@/components/ui/spinner";
|
||||
import { db } from "@lifetracker/db";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export default function TimezoneDisplay() {
|
||||
|
||||
const dbTimezone = useTimezone();
|
||||
const [timezone, setTimezone] = useState(dbTimezone);
|
||||
|
||||
// Update timezone state when dbTimezone changes
|
||||
useEffect(() => {
|
||||
if (dbTimezone !== undefined) {
|
||||
setTimezone(dbTimezone);
|
||||
}
|
||||
}, [dbTimezone]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleTzChange = (event) => {
|
||||
setTimezone(event.detail.timezone);
|
||||
};
|
||||
|
||||
window.addEventListener('timezoneUpdated', handleTzChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('timezoneUpdated', handleTzChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
if (timezone === undefined) {
|
||||
return (
|
||||
<div className="flex flex-col items-center w-full">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="text-center w-full">
|
||||
<Link href="/settings/app">
|
||||
<b className="whitespace-nowrap">{format(TZDate.tz(timezone), 'MMM dd, hh:mm aa')}</b>
|
||||
<br />
|
||||
<span>in </span>
|
||||
<span className="whitespace-nowrap">
|
||||
<b>{timezones[timezone]}</b>
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
apps/web/components/settings/AppSettings.tsx
Normal file
26
apps/web/components/settings/AppSettings.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { api } from "@/server/api/client";
|
||||
import ChangeTimezone from "@/components/settings/ChangeTimezone";
|
||||
|
||||
export default async function AppSettings() {
|
||||
|
||||
const userTimezone: string = await api.users.getTimezone().then((res) => {
|
||||
return res;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mb-8 flex w-full flex-col sm:flex-row">
|
||||
<div className="mb-4 w-full text-lg font-medium sm:w-1/3">
|
||||
Locale Settings
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="mb-2" >
|
||||
<div className="mb-2 text-sm font-medium">Timezone</div>
|
||||
<div className="select-wrapper">
|
||||
<ChangeTimezone
|
||||
userTimezone={userTimezone}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
31
apps/web/components/settings/ChangeTimezone.tsx
Normal file
31
apps/web/components/settings/ChangeTimezone.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { timezones } from '@/lib/timezones';
|
||||
import TimezoneSelect, { type ITimezone } from 'react-timezone-select'
|
||||
import { useUpdateUserTimezone } from "@lifetracker/shared-react/hooks/timezones";
|
||||
|
||||
export default function ChangeTimezone({ userTimezone }) {
|
||||
const [selectedTimezone, setSelectedTimezone] = useState<ITimezone>(userTimezone);
|
||||
const { mutate: updateUserTimezone, isPending } = useUpdateUserTimezone({
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
description: "User DB Timezone updated!",
|
||||
});
|
||||
},
|
||||
});
|
||||
return (
|
||||
|
||||
<TimezoneSelect
|
||||
value={selectedTimezone}
|
||||
onChange={(tz) => {
|
||||
setSelectedTimezone(tz);
|
||||
updateUserTimezone({ newTimezone: tz.value });
|
||||
window.dispatchEvent(new CustomEvent('timezoneUpdated', { detail: { timezone: tz.value } }));
|
||||
}}
|
||||
timezones={{
|
||||
...timezones
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -5,6 +5,7 @@ import { getServerAuthSession } from "@/server/auth";
|
||||
import serverConfig from "@lifetracker/shared/config";
|
||||
|
||||
import { settingsSidebarItems } from "./items";
|
||||
import TimezoneDisplay from "@/components/dashboard/sidebar/TimezoneDisplay";
|
||||
|
||||
export default async function Sidebar() {
|
||||
const session = await getServerAuthSession();
|
||||
@ -27,7 +28,8 @@ export default async function Sidebar() {
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-auto flex items-center border-t pt-2 text-sm text-gray-400">
|
||||
Hoarder v{serverConfig.serverVersion}
|
||||
<TimezoneDisplay />
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
KeyRound,
|
||||
User,
|
||||
Palette,
|
||||
Settings
|
||||
} from "lucide-react";
|
||||
|
||||
export const settingsSidebarItems: {
|
||||
@ -22,6 +23,13 @@ export const settingsSidebarItems: {
|
||||
icon: <User size={18} />,
|
||||
path: "/settings/info",
|
||||
},
|
||||
|
||||
{
|
||||
name: "App Configuration",
|
||||
icon: <Settings size={18} />,
|
||||
path: "/settings/app",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Color Settings",
|
||||
icon: <Palette size={18} />,
|
||||
|
||||
82
apps/web/lib/timezones.ts
Normal file
82
apps/web/lib/timezones.ts
Normal file
@ -0,0 +1,82 @@
|
||||
export const timezones = {
|
||||
// "Pacific/Midway": "Midway Island, Samoa",
|
||||
"Pacific/Honolulu": "Hawaii",
|
||||
// "America/Juneau": "Alaska",
|
||||
"America/Boise": "Mountain Time",
|
||||
// "America/Dawson": "Dawson, Yukon",
|
||||
// "America/Chihuahua": "Chihuahua, La Paz, Mazatlan",
|
||||
// "America/Phoenix": "Arizona",
|
||||
"America/Chicago": "Central Time",
|
||||
// "America/Regina": "Saskatchewan",
|
||||
"America/Mexico_City": "Mexico",
|
||||
// "America/Belize": "Central America",
|
||||
"America/New_York": "Eastern Time",
|
||||
// "America/Bogota": "Bogota, Lima, Quito",
|
||||
// "America/Caracas": "Caracas, La Paz",
|
||||
// "America/Santiago": "Santiago",
|
||||
// "America/St_Johns": "Newfoundland and Labrador",
|
||||
// "America/Sao_Paulo": "Brasilia",
|
||||
// "America/Tijuana": "Mexico",
|
||||
// "America/Montevideo": "Montevideo",
|
||||
"America/Argentina/Buenos_Aires": "Argentina",
|
||||
// "America/Godthab": "Greenland",
|
||||
"America/Los_Angeles": "Pacific Time",
|
||||
// "Atlantic/Azores": "Azores",
|
||||
// "Atlantic/Cape_Verde": "Cape Verde Islands",
|
||||
// GMT: "UTC",
|
||||
"Europe/London": "The UK",
|
||||
"Europe/Dublin": "Ireland",
|
||||
// "Europe/Lisbon": "Lisbon",
|
||||
// "Africa/Casablanca": "Casablanca, Monrovia",
|
||||
// "Atlantic/Canary": "Canary Islands",
|
||||
// "Europe/Belgrade": "Belgrade, Bratislava, Budapest, Ljubljana, Prague",
|
||||
// "Europe/Sarajevo": "Sarajevo, Skopje, Warsaw, Zagreb",
|
||||
// "Europe/Brussels": "Brussels, Copenhagen, Madrid, Paris",
|
||||
"Europe/Amsterdam": "Western Europe",
|
||||
// "Africa/Algiers": "West Central Africa",
|
||||
// "Europe/Bucharest": "Bucharest",
|
||||
// "Africa/Cairo": "Cairo",
|
||||
"Europe/Helsinki": "Eastern Europe",
|
||||
// "Europe/Athens": "Athens",
|
||||
// "Asia/Jerusalem": "Jerusalem",
|
||||
// "Africa/Harare": "Harare, Pretoria",
|
||||
// "Europe/Moscow": "Istanbul, Minsk, Moscow, St. Petersburg, Volgograd",
|
||||
// "Asia/Kuwait": "Kuwait, Riyadh",
|
||||
"Africa/Nairobi": "Kenya",
|
||||
"Africa/Johannesburg": "South Africa",
|
||||
// "Asia/Baghdad": "Baghdad",
|
||||
// "Asia/Tehran": "Tehran",
|
||||
// "Asia/Dubai": "Abu Dhabi, Muscat",
|
||||
// "Asia/Baku": "Baku, Tbilisi, Yerevan",
|
||||
// "Asia/Kabul": "Kabul",
|
||||
// "Asia/Yekaterinburg": "Ekaterinburg",
|
||||
// "Asia/Karachi": "Islamabad, Karachi, Tashkent",
|
||||
"Asia/Kolkata": "India",
|
||||
// "Asia/Kathmandu": "Kathmandu",
|
||||
// "Asia/Dhaka": "Astana, Dhaka",
|
||||
// "Asia/Colombo": "Sri Jayawardenepura",
|
||||
// "Asia/Almaty": "Almaty, Novosibirsk",
|
||||
// "Asia/Rangoon": "Yangon Rangoon",
|
||||
// "Asia/Bangkok": "Bangkok, Hanoi, Jakarta",
|
||||
// "Asia/Krasnoyarsk": "Krasnoyarsk",
|
||||
// "Asia/Shanghai": "Beijing, Chongqing, Hong Kong SAR, Urumqi",
|
||||
// "Asia/Kuala_Lumpur": "Kuala Lumpur, Singapore",
|
||||
// "Asia/Taipei": "Taipei",
|
||||
// "Australia/Perth": "Perth",
|
||||
// "Asia/Irkutsk": "Irkutsk, Ulaanbaatar",
|
||||
// "Asia/Seoul": "Seoul",
|
||||
"Asia/Tokyo": "Japan",
|
||||
// "Asia/Yakutsk": "Yakutsk",
|
||||
// "Australia/Darwin": "Darwin",
|
||||
// "Australia/Adelaide": "Adelaide",
|
||||
// "Australia/Sydney": "Canberra, Melbourne, Sydney",
|
||||
// "Australia/Brisbane": "Brisbane",
|
||||
// "Australia/Hobart": "Hobart",
|
||||
// "Asia/Vladivostok": "Vladivostok",
|
||||
// "Pacific/Guam": "Guam, Port Moresby",
|
||||
// "Asia/Magadan": "Magadan, Solomon Islands, New Caledonia",
|
||||
// "Asia/Kamchatka": "Kamchatka, Marshall Islands",
|
||||
// "Pacific/Fiji": "Fiji Islands",
|
||||
// "Pacific/Auckland": "Auckland, Wellington",
|
||||
// "Pacific/Tongatapu": "Nuku'alofa",
|
||||
};
|
||||
6
apps/web/lib/userLocalSettings/timezone.tsx
Normal file
6
apps/web/lib/userLocalSettings/timezone.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { z } from "zod";
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
export const TimezoneCtx = createContext(undefined);
|
||||
@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/drizzle-adapter": "^1.4.2",
|
||||
"@date-fns/tz": "^1.2.0",
|
||||
"@emoji-mart/data": "^1.1.2",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
@ -70,6 +71,7 @@
|
||||
"react-masonry-css": "^1.0.16",
|
||||
"react-select": "^5.8.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-timezone-select": "^3.2.8",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"request-ip": "^3.3.0",
|
||||
|
||||
@ -71,7 +71,7 @@ CREATE TABLE `hour` (
|
||||
`dayId` text NOT NULL,
|
||||
`categoryId` text,
|
||||
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`dayId`) REFERENCES `day`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
FOREIGN KEY (`dayId`) REFERENCES `day`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`categoryId`) REFERENCES `category`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
@ -89,7 +89,8 @@ CREATE TABLE `user` (
|
||||
`emailVerified` integer,
|
||||
`image` text,
|
||||
`password` text,
|
||||
`role` text DEFAULT 'user'
|
||||
`role` text DEFAULT 'user',
|
||||
`timezone` text DEFAULT 'America/Los_Angeles' NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `verificationToken` (
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "ac3ad6ee-ccd2-4f91-9be7-83bd5b1d9ec8",
|
||||
"id": "170be9e2-c822-4d3a-a0b1-18c8468f1c5d",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"account": {
|
||||
@ -564,7 +564,7 @@
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"hour_categoryId_category_id_fk": {
|
||||
@ -680,6 +680,14 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "'user'"
|
||||
},
|
||||
"timezone": {
|
||||
"name": "timezone",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'America/Los_Angeles'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1732648521848,
|
||||
"tag": "0000_even_mysterio",
|
||||
"when": 1732706352404,
|
||||
"tag": "0000_exotic_dakota_north",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@ -73,6 +73,7 @@ export const users = sqliteTable("user", {
|
||||
image: text("image"),
|
||||
password: text("password"),
|
||||
role: text("role", { enum: ["admin", "user"] }).default("user"),
|
||||
timezone: text("timezone").notNull().default("America/Los_Angeles"),
|
||||
});
|
||||
|
||||
export const accounts = sqliteTable(
|
||||
@ -133,7 +134,6 @@ export const days = sqliteTable("day", {
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
|
||||
});
|
||||
|
||||
export const hours = sqliteTable(
|
||||
@ -149,7 +149,7 @@ export const hours = sqliteTable(
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
comment: text("comment"),
|
||||
time: integer("time").unique(),
|
||||
dayId: text("dayId").notNull().references(() => days.id),
|
||||
dayId: text("dayId").notNull().references(() => days.id, { onDelete: "cascade" }),
|
||||
categoryId: text("categoryId").references(() => categories.id),
|
||||
},
|
||||
(e) => ({
|
||||
|
||||
18
packages/shared-react/hooks/timezones.ts
Normal file
18
packages/shared-react/hooks/timezones.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { api } from "../trpc";
|
||||
|
||||
export function useUpdateUserTimezone(
|
||||
...opts: Parameters<typeof api.users.changeTimezone.useMutation>
|
||||
) {
|
||||
const apiUtils = api.useUtils();
|
||||
return api.users.changeTimezone.useMutation({
|
||||
...opts[0],
|
||||
onSuccess: (res, req, meta) => {
|
||||
return opts[0]?.onSuccess?.(res, req, meta);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useTimezone() {
|
||||
const res = api.users.getTimezone.useQuery().data;
|
||||
return res;
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
|
||||
export function dateFromInput(input: { dateQuery: string }) {
|
||||
let t: string;
|
||||
export function dateFromInput(input: { dateQuery: string, timezone: string }) {
|
||||
let t: TZDate;
|
||||
if (input.dateQuery == "today") {
|
||||
t = TZDate.tz("America/Los_Angeles");
|
||||
t = TZDate.tz(input.timezone);
|
||||
}
|
||||
else {
|
||||
t = new TZDate(input.dateQuery, "Etc/UTC");
|
||||
t = new TZDate(input.dateQuery, input.timezone);
|
||||
}
|
||||
return format(t, "yyyy-MM-dd") + "T00:00:00";
|
||||
}
|
||||
@ -70,7 +70,18 @@
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nextjs-portal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
color: black !important;
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ interface User {
|
||||
name?: string | null | undefined;
|
||||
email?: string | null | undefined;
|
||||
role: "admin" | "user" | null;
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
export interface Context {
|
||||
|
||||
@ -10,6 +10,8 @@ import {
|
||||
import type { Context } from "../index";
|
||||
import { authedProcedure, router } from "../index";
|
||||
import { dateFromInput } from "@lifetracker/shared/utils/days";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
|
||||
async function createDay(date: string, ctx: Context) {
|
||||
return await ctx.db.transaction(async (trx) => {
|
||||
@ -66,8 +68,8 @@ export const daysAppRouter = router({
|
||||
}))
|
||||
.output(zDaySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const date = dateFromInput(input);
|
||||
|
||||
const date = dateFromInput({ timezone: ctx.user.timezone, ...input });
|
||||
console.log(ctx.user.timezone);
|
||||
// Fetch the day data
|
||||
let dayRes;
|
||||
dayRes = await ctx.db
|
||||
@ -123,10 +125,9 @@ export const daysAppRouter = router({
|
||||
if (updatedProps.mood) {
|
||||
updatedProps.mood = parseInt(updatedProps.mood);
|
||||
}
|
||||
console.log(dateQuery, "::", dateFromInput({ dateQuery: dateQuery }));
|
||||
await ctx.db
|
||||
.update(days)
|
||||
.set(updatedProps)
|
||||
.where(eq(days.date, dateFromInput({ dateQuery: dateQuery })));
|
||||
.where(eq(days.date, dateFromInput({ dateQuery: dateQuery, timezone: ctx.user.timezone })));
|
||||
}),
|
||||
});
|
||||
|
||||
@ -147,12 +147,14 @@ export const usersAppRouter = router({
|
||||
})
|
||||
.where(eq(users.id, ctx.user.id));
|
||||
}),
|
||||
|
||||
whoami: authedProcedure
|
||||
.output(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullish(),
|
||||
email: z.string().nullish(),
|
||||
timezone: z.string().nullish(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx }) => {
|
||||
@ -165,6 +167,39 @@ export const usersAppRouter = router({
|
||||
if (!userDb) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
return { id: ctx.user.id, name: ctx.user.name, email: ctx.user.email };
|
||||
return {
|
||||
id: ctx.user.id,
|
||||
name: ctx.user.name,
|
||||
email: ctx.user.email,
|
||||
timezone: ctx.user.timezone,
|
||||
};
|
||||
}),
|
||||
getTimezone: authedProcedure
|
||||
.output(
|
||||
z.string(),
|
||||
)
|
||||
.query(async ({ ctx }) => {
|
||||
const res = await ctx.db.select({ timezone: users.timezone }).from(users).where(eq(users.id, ctx.user.id));
|
||||
|
||||
return res[0].timezone;
|
||||
}),
|
||||
changeTimezone: authedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
newTimezone: z.string(),
|
||||
}),
|
||||
)
|
||||
.output(z.string())
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// invariant(ctx.user.timezone, "A user always has a timezone specified");
|
||||
await ctx.db
|
||||
.update(users)
|
||||
.set({
|
||||
timezone: input.newTimezone,
|
||||
})
|
||||
.where(eq(users.id, ctx.user.id));
|
||||
|
||||
console.log("changeTimezone input", input.newTimezone);
|
||||
return input.newTimezone;
|
||||
}),
|
||||
});
|
||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@ -145,6 +145,9 @@ importers:
|
||||
'@auth/drizzle-adapter':
|
||||
specifier: ^1.4.2
|
||||
version: 1.7.3
|
||||
'@date-fns/tz':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
'@emoji-mart/data':
|
||||
specifier: ^1.1.2
|
||||
version: 1.2.1
|
||||
@ -307,6 +310,9 @@ importers:
|
||||
react-syntax-highlighter:
|
||||
specifier: ^15.5.0
|
||||
version: 15.6.1(react@18.3.1)
|
||||
react-timezone-select:
|
||||
specifier: ^3.2.8
|
||||
version: 3.2.8(react-dom@18.3.1(react@18.3.1))(react-select@5.8.3(@types/react@18.2.61)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
remark-breaks:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
@ -10092,6 +10098,13 @@ packages:
|
||||
peerDependencies:
|
||||
react: '>= 0.14.0'
|
||||
|
||||
react-timezone-select@3.2.8:
|
||||
resolution: {integrity: sha512-efEIVmYAHtm+oS+YlE/9DbieMka1Lop0v1LsW1TdLq0yCBnnAzROKDUY09CICY8TCijZlo0fk+wHZZkV5NpVNw==}
|
||||
peerDependencies:
|
||||
react: ^16 || ^17.0.1 || ^18 || ^19.0.0-0
|
||||
react-dom: ^16 || ^17.0.1 || ^18 || ^19.0.0-0
|
||||
react-select: ^5.8.0
|
||||
|
||||
react-transition-group@4.4.5:
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
@ -10708,6 +10721,9 @@ packages:
|
||||
space-separated-tokens@2.0.2:
|
||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||
|
||||
spacetime@7.6.2:
|
||||
resolution: {integrity: sha512-x5Qr2yiG5wy/0IBeaNeYG9OLigePKTj1/liF9j5xWNbb1psi5Q2AuZTh9SpIT+QZqpKCWX0OCdvwJIm9FlJouA==}
|
||||
|
||||
spdx-correct@3.2.0:
|
||||
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
|
||||
|
||||
@ -11083,6 +11099,9 @@ packages:
|
||||
thunky@1.1.0:
|
||||
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
|
||||
|
||||
timezone-soft@1.5.2:
|
||||
resolution: {integrity: sha512-BUr+CfBfeWXJwFAuEzPO9uF+v6sy3pL5SKLkDg4vdEhsyXgbBnpFoBCW8oEKSNTqNq9YHbVOjNb31xE7WyGmrA==}
|
||||
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
@ -24844,6 +24863,14 @@ snapshots:
|
||||
react: 18.3.1
|
||||
refractor: 3.6.0
|
||||
|
||||
react-timezone-select@3.2.8(react-dom@18.3.1(react@18.3.1))(react-select@5.8.3(@types/react@18.2.61)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-select: 5.8.3(@types/react@18.2.61)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
spacetime: 7.6.2
|
||||
timezone-soft: 1.5.2
|
||||
|
||||
react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
@ -25645,6 +25672,8 @@ snapshots:
|
||||
|
||||
space-separated-tokens@2.0.2: {}
|
||||
|
||||
spacetime@7.6.2: {}
|
||||
|
||||
spdx-correct@3.2.0:
|
||||
dependencies:
|
||||
spdx-expression-parse: 3.0.1
|
||||
@ -26099,6 +26128,8 @@ snapshots:
|
||||
|
||||
thunky@1.1.0: {}
|
||||
|
||||
timezone-soft@1.5.2: {}
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tiny-queue@0.2.1: {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user