Calendar events from DB initial

This commit is contained in:
Kalle
2022-07-22 23:20:39 +03:00
parent bf291cc5a0
commit abc30fd12b
10 changed files with 191 additions and 7 deletions

View File

@@ -2,10 +2,12 @@ import * as users from "./models/users.server";
import * as plusSuggestions from "./models/plusSuggestions.server";
import * as plusVotes from "./models/plusVotes.server";
import * as badges from "./models/badges.server";
import * as calendar from "./models/calendar.server";
export const db = {
users,
plusSuggestions,
plusVotes,
badges,
calendar,
};

View File

@@ -0,0 +1,32 @@
import { dateToDatabaseTimestamp } from "~/utils/dates";
import { sql } from "../sql";
import type { CalendarEvent, CalendarEventDate } from "../types";
const findAllBetweenTwoTimestampsStm = sql.prepare(`
select
"CalendarEvent"."name",
"CalendarEvent"."discordUrl",
"CalendarEvent"."bracketUrl",
"CalendarEventDate"."id",
"CalendarEventDate"."startTime"
from "CalendarEvent"
join "CalendarEventDate" on "CalendarEvent"."id" = "CalendarEventDate"."eventId"
where "CalendarEventDate"."startTime" between $startTime and $endTime
order by "CalendarEventDate"."startTime" asc
`);
export function findAllBetweenTwoTimestamps({
startTime,
endTime,
}: {
startTime: Date;
endTime: Date;
}) {
return findAllBetweenTwoTimestampsStm.all({
startTime: dateToDatabaseTimestamp(startTime),
endTime: dateToDatabaseTimestamp(endTime),
}) as Array<
Pick<CalendarEvent, "name" | "discordUrl" | "bracketUrl"> &
Pick<CalendarEventDate, "id" | "startTime">
>;
}

View File

@@ -10,6 +10,7 @@ import type { UpsertManyPlusVotesArgs } from "./models/plusVotes.server";
import { ADMIN_DISCORD_ID } from "~/constants";
import shuffle from "just-shuffle";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import capitalize from "just-capitalize";
const ADMIN_TEST_AVATAR = "fcfd65a3bea598905abb9ca25296816b";
@@ -28,6 +29,7 @@ const basicSeeds = [
badgesToUsers,
badgeManagers,
patrons,
calendarEvents,
];
export function seed() {
@@ -40,6 +42,10 @@ export function seed() {
function wipeDB() {
const tablesToDelete = [
"CalendarEventDate",
"CalendarEventWinner",
"CalendarEventBadge",
"CalendarEvent",
"User",
"PlusVote",
"PlusSuggestion",
@@ -299,3 +305,87 @@ function patrons() {
});
}
}
function calendarEvents() {
const userIds = sql
.prepare(`select "id" from "User" order by random()`)
.all()
.map((u) => u.id);
for (let id = 1; id <= 100; id++) {
sql
.prepare(
`
insert into "CalendarEvent" (
"id",
"name",
"description",
"discordUrl",
"bracketUrl",
"authorId"
) values (
$id,
$name,
$description,
$discordUrl,
$bracketUrl,
$authorId
)
`
)
.run({
id,
name: `${capitalize(faker.word.adjective())} ${capitalize(
faker.word.noun()
)}`,
description: faker.lorem.paragraph(),
discordUrl: faker.internet.url(),
bracketUrl: faker.internet.url(),
authorId: userIds.pop(),
});
const twoDayEvent = Math.random() > 0.9;
const startTime = faker.date.soon(42);
startTime.setMinutes(0, 0, 0);
sql
.prepare(
`
insert into "CalendarEventDate" (
"eventId",
"startTime"
) values (
$eventId,
$startTime
)
`
)
.run({
eventId: id,
startTime: dateToDatabaseTimestamp(startTime),
});
if (twoDayEvent) {
startTime.setDate(startTime.getDate() + 1);
sql
.prepare(
`
insert into "CalendarEventDate" (
"eventId",
"startTime"
) values (
$eventId,
$startTime
)
`
)
.run({
eventId: id,
startTime: dateToDatabaseTimestamp(startTime),
});
}
// xxx: 20% of cases have 1-2 badge prizes
}
}

View File

@@ -88,6 +88,7 @@ export interface CalendarEvent {
}
export interface CalendarEventDate {
id: number;
eventId: number;
startTime: number;
}

View File

@@ -1,12 +1,19 @@
import type { LoaderArgs } from "@remix-run/node";
import { json, type LinksFunction } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import type { UseDataFunctionReturn } from "@remix-run/react/dist/components";
import { addDays, subDays } from "date-fns";
import { Flipped, Flipper } from "react-flip-toolkit";
import { z } from "zod";
import { Main } from "~/components/Main";
import { db } from "~/db";
import styles from "~/styles/calendar.css";
import { dateToWeekNumber, weekNumberToDate } from "~/utils/dates";
import {
databaseTimestampToDate,
dateToWeekNumber,
weekNumberToDate,
} from "~/utils/dates";
import type { Unpacked } from "~/utils/types";
import { actualNumber } from "~/utils/zod";
export const links: LinksFunction = () => {
@@ -36,8 +43,6 @@ export const loader = ({ request }: LoaderArgs) => {
? parsedParams.data.year
: now.getFullYear();
console.log({ weekToFetch, yearToFetch, thisWeek }); // TODO: db query to get events of this week
return json({
thisWeek,
weeks: closeByWeeks({ week: weekToFetch, year: yearToFetch }).map(
@@ -46,6 +51,7 @@ export const loader = ({ request }: LoaderArgs) => {
numberOfEvents: 12,
})
),
events: fetchEventsOfWeek({ week: weekToFetch, year: yearToFetch }),
});
};
@@ -65,10 +71,26 @@ function closeByWeeks(args: { week: number; year: number }) {
});
}
function fetchEventsOfWeek(args: { week: number; year: number }) {
const startTime = weekNumberToDate(args);
const endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 7);
// so we get all events of sunday even from US west coast perspective
endTime.setHours(endTime.getHours() + 12);
return db.calendar.findAllBetweenTwoTimestamps({ startTime, endTime });
}
export default function CalendarPage() {
const data = useLoaderData<typeof loader>();
return (
<Main>
<WeekLinks />
{data.events.map((event) => {
return <Event key={event.id} event={event} />;
})}
</Main>
);
}
@@ -121,3 +143,21 @@ function WeekLinks() {
</Flipper>
);
}
function Event({
event,
}: {
event: Unpacked<UseDataFunctionReturn<typeof loader>["events"]>;
}) {
return (
<section className="calendar__event">
<time>
{databaseTimestampToDate(event.startTime).toLocaleTimeString("en", {
hour: "numeric",
minute: "numeric",
})}
</time>{" "}
{event.name}
</section>
);
}

View File

@@ -70,3 +70,7 @@
.calendar__week:nth-child(6) > .calendar__event-count {
font-size: var(--fonts-xxs);
}
.calendar__event {
font-size: var(--fonts-lg);
}

View File

@@ -20,7 +20,9 @@ export function weekNumberToDate({
week: number;
year: number;
}) {
const result = new Date(year, 0, 4);
// xxx: possible problem of mismatch when server time and local time don't match
// gotta make sure events which belong to monday are still shown for sunday
const result = new Date(Date.UTC(year, 0, 4));
result.setDate(
result.getDate() - (result.getDay() || 7) + 1 + 7 * (week - 1)
);

View File

@@ -17,7 +17,8 @@ module.exports.up = function (db) {
db.prepare(
`
create table "CalendarEventDate" (
"eventId" integer primary key,
"id" integer primary key,
"eventId" integer not null,
"startTime" integer not null,
foreign key ("eventId") references "CalendarEvent"("id") on delete cascade
) strict
@@ -27,7 +28,7 @@ module.exports.up = function (db) {
db.prepare(
`
create table "CalendarEventWinner" (
"eventId" integer primary key,
"eventId" integer not null,
"teamName" text not null,
"placement" integer not null,
"userId" integer,
@@ -41,7 +42,7 @@ module.exports.up = function (db) {
db.prepare(
`
create table "CalendarEventBadge" (
"eventId" integer primary key,
"eventId" integer not null,
"badgeId" integer not null,
foreign key ("eventId") references "CalendarEvent"("id") on delete cascade,
foreign key ("badgeId") references "Badge"("id") on delete restrict,

11
package-lock.json generated
View File

@@ -21,6 +21,7 @@
"i18next-browser-languagedetector": "^6.1.4",
"i18next-fs-backend": "^1.1.4",
"i18next-http-backend": "^1.4.1",
"just-capitalize": "^3.0.1",
"just-shuffle": "^4.0.1",
"node-cron": "3.0.0",
"react": "^18.2.0",
@@ -9484,6 +9485,11 @@
"node": ">=4.0"
}
},
"node_modules/just-capitalize": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/just-capitalize/-/just-capitalize-3.0.1.tgz",
"integrity": "sha512-GPvagN7Y2IdNFdFh8wRtE0AoxjA+0zAOF+XS8i2Di+aNdTZ0SAb5bARqjklOxVgIUTBfTv6MqqrRmdGjnLbkQA=="
},
"node_modules/just-shuffle": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/just-shuffle/-/just-shuffle-4.0.1.tgz",
@@ -22397,6 +22403,11 @@
"object.assign": "^4.1.2"
}
},
"just-capitalize": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/just-capitalize/-/just-capitalize-3.0.1.tgz",
"integrity": "sha512-GPvagN7Y2IdNFdFh8wRtE0AoxjA+0zAOF+XS8i2Di+aNdTZ0SAb5bARqjklOxVgIUTBfTv6MqqrRmdGjnLbkQA=="
},
"just-shuffle": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/just-shuffle/-/just-shuffle-4.0.1.tgz",

View File

@@ -42,6 +42,7 @@
"i18next-browser-languagedetector": "^6.1.4",
"i18next-fs-backend": "^1.1.4",
"i18next-http-backend": "^1.4.1",
"just-capitalize": "^3.0.1",
"just-shuffle": "^4.0.1",
"node-cron": "3.0.0",
"react": "^18.2.0",