From fc5228a397dcb031115c6abb5a977f2d23297f95 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Wed, 25 Jan 2023 20:36:50 -0500 Subject: [PATCH 01/15] fix(songs): restricting the fetching of songs to just the ones that the user created for now Not sure if this is what we were thinking of for the MVP or if it was going to be a free for all and users could use eachothers songs. --- app/db/song.db.ts | 9 +++++++-- app/routes/__app/songs/$id/index.tsx | 2 +- app/routes/__app/songs/index.tsx | 5 ++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/db/song.db.ts b/app/db/song.db.ts index 1df5ec5..d27be8a 100644 --- a/app/db/song.db.ts +++ b/app/db/song.db.ts @@ -24,8 +24,13 @@ export async function getSong({ id }: Pick) { }); } -export async function getSongs() { - return await prisma.song.findMany(); +/** + * Gets the songs of the currently logged in user + * @param userId + * @returns + */ +export async function getUserSongs(userId: number) { + return await prisma.song.findMany({ where: { authorId: userId } }); } export async function createSong({ authorId }: Pick) { diff --git a/app/routes/__app/songs/$id/index.tsx b/app/routes/__app/songs/$id/index.tsx index f6e02ae..3b8885a 100644 --- a/app/routes/__app/songs/$id/index.tsx +++ b/app/routes/__app/songs/$id/index.tsx @@ -19,7 +19,7 @@ export async function loader({ params }: LoaderArgs) { export default function () { const { song } = useTypedLoaderData(); - console.log(song.stanzas); + return (
diff --git a/app/routes/__app/songs/index.tsx b/app/routes/__app/songs/index.tsx index ecf2cd5..d09ef3b 100644 --- a/app/routes/__app/songs/index.tsx +++ b/app/routes/__app/songs/index.tsx @@ -5,14 +5,13 @@ import { Form, Link } from "@remix-run/react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { Button } from "~/components/Button"; import { Search } from "~/components/Search"; -import { getSongs } from "~/db/song.db"; +import { getUserSongs } from "~/db/song.db"; import { useSearch } from "~/lib/useSearch"; import { requireAuthentication } from "~/utils/auth.server"; export async function loader({ request }: LoaderArgs) { const user = await requireAuthentication(request); - console.log(user); - return typedjson({ songs: await getSongs() }); + return typedjson({ songs: await getUserSongs(user.id) }); } export default function SongsIndex() { From 0d85de04ac47c3a4c0936b0b1abcc70112d98cc7 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 09:01:54 -0500 Subject: [PATCH 02/15] feat(db): adding author for setlist a user should be able to only get their created setlists. more than likely this will be more of an organization or band hierarchy kinda thing but for mvp this will work. --- prisma/schema.prisma | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c9ba939..3732eb0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,14 +11,15 @@ datasource db { } model User { - id Int @id @default(autoincrement()) - email String @unique + id Int @id @default(autoincrement()) + email String @unique name String? // role Role @default(USER) songs Song[] profile Profile? oAuthProvider String? oAuthId String? + setlists Setlist[] } model Profile { @@ -38,32 +39,31 @@ model Song { published Boolean @default(false) author User @relation(fields: [authorId], references: [id]) authorId Int + // time in seconds for the length of song + runtime Int @default(185) // bonus points if you know the reference categories Category[] //# @relation(references: [id]) - Setlist Setlist? @relation(fields: [setlistId], references: [id]) - setlistId String? + setlists Setlist[] // @relation(fields: [setlistId], references: [id]) } model Category { id Int @id @default(autoincrement()) name String - songs Song[] //@relation(references: [id]) + songs Song[] // @relation(references: [id]) setlists Setlist[] } model Setlist { - id String @id @default(uuid()) - name String @default("") - description String @default("") - // Json type not supported in SQLite - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - qrcode String @default("") - - songs Song[] - Category Category? @relation(fields: [categoryId], references: [id]) - categoryId Int? + id String @id @default(uuid()) + name String @default("") + description String @default("") + author User @relation(fields: [authorId], references: [id]) + authorId Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + qrcode String @default("") + songs Song[] + categories Category[] } // enum Role { From 1cef83e13bbafe45ca52181478409ad5d1f1c05a Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 09:03:08 -0500 Subject: [PATCH 03/15] fix(ap): restricting fetching of setlists to only the users created setlist --- app/db/setlist.db.ts | 15 ++++++++++++++- app/routes/__app/setlists/index.tsx | 10 +++++++--- app/routes/__app/setlists/new.tsx | 4 ++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/db/setlist.db.ts b/app/db/setlist.db.ts index 3daf806..ed70b31 100644 --- a/app/db/setlist.db.ts +++ b/app/db/setlist.db.ts @@ -23,8 +23,21 @@ export async function getSetlists() { }); } +export async function getMySetlists(userId: number) { + return await prisma.setlist.findMany({ + where: { authorId: userId }, + include: { + songs: { + select: { + id: true, + }, + }, + }, + }); +} + export async function createSetlist( - data?: Pick + data?: Pick ) { return await prisma.setlist.create({ data: { ...data }, diff --git a/app/routes/__app/setlists/index.tsx b/app/routes/__app/setlists/index.tsx index eb7178e..c3bb56b 100644 --- a/app/routes/__app/setlists/index.tsx +++ b/app/routes/__app/setlists/index.tsx @@ -1,13 +1,17 @@ import type { Setlist } from "@prisma/client"; import { Link } from "@remix-run/react"; +import type { LoaderArgs } from "@remix-run/node"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { Button } from "~/components/Button"; import { Search } from "~/components/Search"; -import { getSetlists } from "~/db/setlist.db"; import { useSearch } from "~/lib/useSearch"; +import { requireAuthentication } from "~/utils/auth.server"; +import { getMySetlists } from "~/db/setlist.db"; -export async function loader() { - const setlists = await getSetlists(); +export async function loader({ request }: LoaderArgs) { + const user = await requireAuthentication(request); + + const setlists = await getMySetlists(user.id); return typedjson({ setlists, }); diff --git a/app/routes/__app/setlists/new.tsx b/app/routes/__app/setlists/new.tsx index 8cd45a3..4f0f553 100644 --- a/app/routes/__app/setlists/new.tsx +++ b/app/routes/__app/setlists/new.tsx @@ -3,8 +3,11 @@ import { redirect } from "@remix-run/node"; import { typedjson } from "remix-typedjson"; import { createSetlist, setQRCode } from "~/db/setlist.db"; import { formDataToJson } from "~/helpers/formDataToJson"; +import { currentAuthedUser } from "~/utils/auth.server"; export async function action({ request }: ActionArgs) { + const user = await currentAuthedUser(request); + if (!user) return redirect("/"); const formData = formDataToJson(await request.formData()); if (!formData.name) return; @@ -12,6 +15,7 @@ export async function action({ request }: ActionArgs) { const setlist = await createSetlist({ name: formData.name as string, description: formData?.description as string, + authorId: user.id as number, }); await setQRCode(setlist, request); return redirect(`/setlists`); From 4a822e8e90213085fd19010e1d19997aeddd2912 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:28:55 -0500 Subject: [PATCH 04/15] feat(db): adding new fields for song usage --- prisma/schema.prisma | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3732eb0..5697865 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -30,19 +30,21 @@ model Profile { } model Song { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - title String - attribution String @default("") - stanzas String - published Boolean @default(false) - author User @relation(fields: [authorId], references: [id]) - authorId Int + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String + attribution String @default("") + stanzas String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int // time in seconds for the length of song - runtime Int @default(185) // bonus points if you know the reference - categories Category[] //# @relation(references: [id]) - setlists Setlist[] // @relation(fields: [setlistId], references: [id]) + runtime Int @default(185) // bonus points if you know the reference + notes String @default("") + claimedPublicDomain Boolean @default(false) + categories Category[] //# @relation(references: [id]) + setlists Setlist[] // @relation(fields: [setlistId], references: [id]) } model Category { From 42d3af5e3fe3294eff1fae9e837cbce138cbad20 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:29:55 -0500 Subject: [PATCH 05/15] test: adding some basic working data in prisma added song in user id 3 and some categories --- prisma/seed.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/prisma/seed.ts b/prisma/seed.ts index 2132e27..444fab1 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -25,6 +25,31 @@ async function main() { name: "Keith Full Auth Flow", }, }); + await prisma.category.create({ data: { name: "Sea Shanties" } }); + await prisma.category.create({ data: { name: "New American Americana" } }); + await prisma.category.create({ data: { name: "Folk" } }); + await prisma.category.create({ data: { name: "Protest" } }); + await prisma.category.create({ + data: { name: "Really makes you go hmmmmm." }, + }); + await prisma.category.create({ + data: { name: "Really makes you go hummmmm." }, + }); + + await prisma.song.create({ + data: { + id: 1, + createdAt: "2023-01-26T12:41:28.070Z", + updatedAt: "2023-01-26T12:41:56.718Z", + title: "test", + attribution: "te", + stanzas: + '[{"type":"verse","id":"hocze1qhqldd33p4n","lines":[{"id":"hocze1qhqldd33p4o","lyrics":"te","notes":""},{"id":"ldd33sdr","lyrics":"test","notes":""}]}]', + published: false, + authorId: 3, + }, + }); + // await prisma.category.upsert console.log({ keith, drew, keithFullAuth }); } main() From 4dfd633402d8f4d8af604711fc642c906c9efc78 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:32:51 -0500 Subject: [PATCH 06/15] chore(scripts): db:hard-refresh for destroying the database and reseed faster local dev interations for when the database needs to be hard reset and starting from scratch. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 24d89f6..4cfc177 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "db:deploy": "npx prisma migrate deploy", "db:push": "npx prisma db push", "db:reset": "rm -r prisma/data.db && npm run db:push", + "db:hard-refresh": "rm -r prisma/data.db && npm run db:push && npm run db:seed", "db:seed": "npx prisma db seed", "db:studio": "npx prisma studio -p 7777 --browser none", "dev": "concurrently \"npm run dev:css\" \"remix dev\"", From c55e26a81721647d91bba46d0018e720accb4b34 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:34:49 -0500 Subject: [PATCH 07/15] feat(helpers): bringing over some time formatting The format time was from our implementation in lumastic-app, the format runtime is for computing the friendly version of the stored seconds of runtime. --- app/helpers/formatTime.test.ts | 31 +++++++++++++++++++++++++++++ app/helpers/formatTime.ts | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 app/helpers/formatTime.test.ts create mode 100644 app/helpers/formatTime.ts diff --git a/app/helpers/formatTime.test.ts b/app/helpers/formatTime.test.ts new file mode 100644 index 0000000..791fd36 --- /dev/null +++ b/app/helpers/formatTime.test.ts @@ -0,0 +1,31 @@ +import formatTime from "./formatTime"; + +describe("Time ago tests", () => { + test("less than 1m ago", () => { + expect( + formatTime({ + time: new Date() - 1, + }) + ).toStrictEqual("a few seconds ago"); + }); +}); + +describe("additional format testing", () => { + test("full date", () => { + expect( + formatTime({ + time: "2020-04-06T18:19:00.000Z", + fullDate: true, + }) + ).toStrictEqual("April 6, 2020"); + }); + test("full date and time", () => { + expect( + formatTime({ + time: "2020-04-06T18:19:00.000Z", + fullDate: true, + withTime: true, + }) + ).toStrictEqual("April 6, 2020 2:19 PM"); + }); +}); diff --git a/app/helpers/formatTime.ts b/app/helpers/formatTime.ts new file mode 100644 index 0000000..92f310f --- /dev/null +++ b/app/helpers/formatTime.ts @@ -0,0 +1,36 @@ +import moment from "moment"; + +/** + * Implements Moment.js for formatting dates + * https://momentjs.com/docs/#/displaying/fromnow/ + * + * @param {{time:Date}} param0 input parameters + */ +const formatTime = ({ + time = "", + fullDate = false, + withTime = false, + ago = true, +}) => { + if (fullDate) { + let format = "LL"; + if (withTime) { + format += "L"; + } + return moment(time).format(format); + } + return moment(time).fromNow(!ago); +}; + +export default formatTime; + +/** + * formats the runtime in seconds to US-EN format. + */ +export const formatRunTime = (runtime: number) => { + if (runtime < 60) return `${runtime} sec.`; + let min = runtime / 60; + min = ~~min; + const remSec = runtime % 60; + return `${min} min. ${remSec} sec.`; +}; From aa29c69dbc8b92f7bfb7ad659a12e3eac6f976f8 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:36:29 -0500 Subject: [PATCH 08/15] build(package.json): adding momentjs for display of local time this should be evaluated and removed if necessary, i believe our implementation of the library is probably covered in standard JS at this time. --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index 236ec1c..4d492ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "debounce": "^1.2.1", "dotenv-cli": "^6.0.0", "fuse.js": "^6.6.2", + "moment": "^2.28.0", "qrcode": "^1.5.1", "qs": "^6.11.0", "react": "^18.2.0", @@ -13531,6 +13532,14 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -31317,6 +31326,11 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", diff --git a/package.json b/package.json index 4cfc177..a5220be 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "debounce": "^1.2.1", "dotenv-cli": "^6.0.0", "fuse.js": "^6.6.2", + "moment": "^2.28.0", "qrcode": "^1.5.1", "qs": "^6.11.0", "react": "^18.2.0", From 2f2c8ec1f27b584f0d8fd3ca56f39d7bd94513e5 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:55:19 -0500 Subject: [PATCH 09/15] removing claimedPublicDomain at this time was having too many issues with the form updating and what not. Proabbly have to be a controlled input? --- app/db/song.db.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/db/song.db.ts b/app/db/song.db.ts index d27be8a..49b0e7e 100644 --- a/app/db/song.db.ts +++ b/app/db/song.db.ts @@ -49,7 +49,7 @@ export async function updateSong({ data, }: { id: Song["id"]; - data: Pick; + data: Pick; }) { return await prisma.song.update({ where: { id: +id }, data }); } From 76cec0910a034efff49d51fc381bf31b43ad2963 Mon Sep 17 00:00:00 2001 From: Keith Stolte Date: Thu, 26 Jan 2023 12:55:58 -0500 Subject: [PATCH 10/15] updating form for supporting new fields for song editor --- app/components/forms/song/SongForm.tsx | 20 ++++++++++++++++++++ app/routes/__app/songs/$id/edit.tsx | 7 +++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/app/components/forms/song/SongForm.tsx b/app/components/forms/song/SongForm.tsx index 1cf0dce..0ab7d3a 100644 --- a/app/components/forms/song/SongForm.tsx +++ b/app/components/forms/song/SongForm.tsx @@ -60,6 +60,26 @@ const SongForm = forwardRef<
))}
+