From 9a26d9720902d0d5a99e649b320c134ba2508109 Mon Sep 17 00:00:00 2001 From: Simon Zeyer Date: Sun, 5 Nov 2023 21:35:01 +0000 Subject: [PATCH] Init DB and Tests with Components - add Artikel, Kauf, Kategorie Model - generate Components - playing with custom components - add Tailwindcss --- .vscode/extensions.json | 6 +- .../migrations/20231105162730_/migration.sql | 24 ++ .../migrations/20231105165906_/migration.sql | 49 +++ .../migrations/20231105172818_/migration.sql | 47 +++ api/db/migrations/migration_lock.toml | 3 + api/db/schema.prisma | 36 +- api/src/graphql/artikels.sdl.js | 33 ++ api/src/graphql/kategories.sdl.js | 27 ++ api/src/graphql/kaufArtikels.sdl.js | 34 ++ api/src/graphql/kaufs.sdl.js | 29 ++ api/src/services/artikels/artikels.js | 39 ++ .../services/artikels/artikels.scenarios.js | 6 + api/src/services/artikels/artikels.test.js | 54 +++ api/src/services/kategories/kategories.js | 36 ++ .../kategories/kategories.scenarios.js | 6 + .../services/kategories/kategories.test.js | 56 +++ api/src/services/kaufArtikels/kaufArtikels.js | 39 ++ .../kaufArtikels/kaufArtikels.scenarios.js | 18 + .../kaufArtikels/kaufArtikels.test.js | 62 +++ api/src/services/kaufs/kaufs.js | 36 ++ api/src/services/kaufs/kaufs.scenarios.js | 3 + api/src/services/kaufs/kaufs.test.js | 28 ++ package.json | 3 +- prettier.config.js | 2 + web/config/postcss.config.js | 8 + web/config/tailwind.config.js | 9 + web/package.json | 7 +- web/src/App.jsx | 1 + web/src/Routes.jsx | 27 +- web/src/components/.keep | 0 .../components/Artikel/Artikel/Artikel.jsx | 80 ++++ .../Artikel/ArtikelCell/ArtikelCell.jsx | 24 ++ .../Artikel/ArtikelForm/ArtikelForm.jsx | 102 +++++ .../components/Artikel/Artikels/Artikels.jsx | 90 ++++ .../Artikel/ArtikelsCell/ArtikelsCell.jsx | 35 ++ .../Artikel/ArtikelsPos/ArtikelsPos.jsx | 103 +++++ .../ArtikelsPosCell/ArtikelsPosCell.jsx | 35 ++ .../EditArtikelCell/EditArtikelCell.jsx | 75 ++++ .../Artikel/NewArtikelCell/NewArtikelCell.jsx | 45 ++ .../EditKategorieCell/EditKategorieCell.jsx | 66 +++ .../Kategorie/Kategorie/Kategorie.jsx | 72 ++++ .../Kategorie/KategorieCell/KategorieCell.jsx | 22 + .../Kategorie/KategorieForm/KategorieForm.jsx | 53 +++ .../KategorieSelect/KategoriesSelectCell.jsx | 44 ++ .../Kategorie/Kategories/Kategories.jsx | 86 ++++ .../KategoriesCell/KategoriesCell.jsx | 33 ++ .../Kategorie/NewKategorie/NewKategorie.jsx | 45 ++ .../Kauf/EditKaufCell/EditKaufCell.jsx | 60 +++ web/src/components/Kauf/Kauf/Kauf.jsx | 76 ++++ web/src/components/Kauf/KaufCell/KaufCell.jsx | 23 + web/src/components/Kauf/KaufForm/KaufForm.jsx | 53 +++ web/src/components/Kauf/Kaufs/Kaufs.jsx | 27 ++ .../components/Kauf/KaufsCell/KaufsCell.jsx | 34 ++ .../Kauf/KaufsPosCell/KaufsPosCell.jsx | 60 +++ web/src/components/Kauf/NewKauf/NewKauf.jsx | 42 ++ web/src/index.css | 13 + web/src/layouts/PosLayout/PosLayout.jsx | 26 ++ web/src/layouts/PosLayout/ScaffoldLayout.jsx | 26 ++ .../layouts/ScaffoldLayout/ScaffoldLayout.jsx | 29 ++ web/src/lib/formatters.jsx | 58 +++ web/src/lib/formatters.test.jsx | 192 +++++++++ .../pages/Artikel/ArtikelPage/ArtikelPage.jsx | 7 + .../Artikel/ArtikelsPage/ArtikelsPage.jsx | 7 + .../EditArtikelPage/EditArtikelPage.jsx | 7 + .../Artikel/NewArtikelPage/NewArtikelPage.jsx | 7 + .../EditKategoriePage/EditKategoriePage.jsx | 7 + .../Kategorie/KategoriePage/KategoriePage.jsx | 7 + .../KategoriesPage/KategoriesPage.jsx | 7 + .../NewKategoriePage/NewKategoriePage.jsx | 7 + .../pages/Kauf/EditKaufPage/EditKaufPage.jsx | 7 + web/src/pages/Kauf/KaufPage/KaufPage.jsx | 7 + web/src/pages/Kauf/KaufsPage/KaufsPage.jsx | 7 + .../pages/Kauf/NewKaufPage/NewKaufPage.jsx | 7 + web/src/pages/PosPage/PosPage.jsx | 21 +- web/src/scaffold.css | 397 ++++++++++++++++++ yarn.lock | 381 ++++++++++++++++- 76 files changed, 3308 insertions(+), 32 deletions(-) create mode 100644 api/db/migrations/20231105162730_/migration.sql create mode 100644 api/db/migrations/20231105165906_/migration.sql create mode 100644 api/db/migrations/20231105172818_/migration.sql create mode 100644 api/db/migrations/migration_lock.toml create mode 100644 api/src/graphql/artikels.sdl.js create mode 100644 api/src/graphql/kategories.sdl.js create mode 100644 api/src/graphql/kaufArtikels.sdl.js create mode 100644 api/src/graphql/kaufs.sdl.js create mode 100644 api/src/services/artikels/artikels.js create mode 100644 api/src/services/artikels/artikels.scenarios.js create mode 100644 api/src/services/artikels/artikels.test.js create mode 100644 api/src/services/kategories/kategories.js create mode 100644 api/src/services/kategories/kategories.scenarios.js create mode 100644 api/src/services/kategories/kategories.test.js create mode 100644 api/src/services/kaufArtikels/kaufArtikels.js create mode 100644 api/src/services/kaufArtikels/kaufArtikels.scenarios.js create mode 100644 api/src/services/kaufArtikels/kaufArtikels.test.js create mode 100644 api/src/services/kaufs/kaufs.js create mode 100644 api/src/services/kaufs/kaufs.scenarios.js create mode 100644 api/src/services/kaufs/kaufs.test.js create mode 100644 web/config/postcss.config.js create mode 100644 web/config/tailwind.config.js delete mode 100644 web/src/components/.keep create mode 100644 web/src/components/Artikel/Artikel/Artikel.jsx create mode 100644 web/src/components/Artikel/ArtikelCell/ArtikelCell.jsx create mode 100644 web/src/components/Artikel/ArtikelForm/ArtikelForm.jsx create mode 100644 web/src/components/Artikel/Artikels/Artikels.jsx create mode 100644 web/src/components/Artikel/ArtikelsCell/ArtikelsCell.jsx create mode 100644 web/src/components/Artikel/ArtikelsPos/ArtikelsPos.jsx create mode 100644 web/src/components/Artikel/ArtikelsPosCell/ArtikelsPosCell.jsx create mode 100644 web/src/components/Artikel/EditArtikelCell/EditArtikelCell.jsx create mode 100644 web/src/components/Artikel/NewArtikelCell/NewArtikelCell.jsx create mode 100644 web/src/components/Kategorie/EditKategorieCell/EditKategorieCell.jsx create mode 100644 web/src/components/Kategorie/Kategorie/Kategorie.jsx create mode 100644 web/src/components/Kategorie/KategorieCell/KategorieCell.jsx create mode 100644 web/src/components/Kategorie/KategorieForm/KategorieForm.jsx create mode 100644 web/src/components/Kategorie/KategorieSelect/KategoriesSelectCell.jsx create mode 100644 web/src/components/Kategorie/Kategories/Kategories.jsx create mode 100644 web/src/components/Kategorie/KategoriesCell/KategoriesCell.jsx create mode 100644 web/src/components/Kategorie/NewKategorie/NewKategorie.jsx create mode 100644 web/src/components/Kauf/EditKaufCell/EditKaufCell.jsx create mode 100644 web/src/components/Kauf/Kauf/Kauf.jsx create mode 100644 web/src/components/Kauf/KaufCell/KaufCell.jsx create mode 100644 web/src/components/Kauf/KaufForm/KaufForm.jsx create mode 100644 web/src/components/Kauf/Kaufs/Kaufs.jsx create mode 100644 web/src/components/Kauf/KaufsCell/KaufsCell.jsx create mode 100644 web/src/components/Kauf/KaufsPosCell/KaufsPosCell.jsx create mode 100644 web/src/components/Kauf/NewKauf/NewKauf.jsx create mode 100644 web/src/layouts/PosLayout/PosLayout.jsx create mode 100644 web/src/layouts/PosLayout/ScaffoldLayout.jsx create mode 100644 web/src/layouts/ScaffoldLayout/ScaffoldLayout.jsx create mode 100644 web/src/lib/formatters.jsx create mode 100644 web/src/lib/formatters.test.jsx create mode 100644 web/src/pages/Artikel/ArtikelPage/ArtikelPage.jsx create mode 100644 web/src/pages/Artikel/ArtikelsPage/ArtikelsPage.jsx create mode 100644 web/src/pages/Artikel/EditArtikelPage/EditArtikelPage.jsx create mode 100644 web/src/pages/Artikel/NewArtikelPage/NewArtikelPage.jsx create mode 100644 web/src/pages/Kategorie/EditKategoriePage/EditKategoriePage.jsx create mode 100644 web/src/pages/Kategorie/KategoriePage/KategoriePage.jsx create mode 100644 web/src/pages/Kategorie/KategoriesPage/KategoriesPage.jsx create mode 100644 web/src/pages/Kategorie/NewKategoriePage/NewKategoriePage.jsx create mode 100644 web/src/pages/Kauf/EditKaufPage/EditKaufPage.jsx create mode 100644 web/src/pages/Kauf/KaufPage/KaufPage.jsx create mode 100644 web/src/pages/Kauf/KaufsPage/KaufsPage.jsx create mode 100644 web/src/pages/Kauf/NewKaufPage/NewKaufPage.jsx create mode 100644 web/src/scaffold.css diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7fc50a1..6e458a9 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -8,7 +8,9 @@ "pflannery.vscode-versionlens", "editorconfig.editorconfig", "prisma.prisma", - "graphql.vscode-graphql" + "graphql.vscode-graphql", + "csstools.postcss", + "bradlc.vscode-tailwindcss" ], "unwantedRecommendations": [] -} +} \ No newline at end of file diff --git a/api/db/migrations/20231105162730_/migration.sql b/api/db/migrations/20231105162730_/migration.sql new file mode 100644 index 0000000..42b4de4 --- /dev/null +++ b/api/db/migrations/20231105162730_/migration.sql @@ -0,0 +1,24 @@ +-- CreateTable +CREATE TABLE "artikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "preis" REAL +); + +-- CreateTable +CREATE TABLE "kauf" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "timestamp" INTEGER NOT NULL, + "preis_ges" REAL +); + +-- CreateTable +CREATE TABLE "kauf_artikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "kauf_id" INTEGER NOT NULL, + "artikel_id" INTEGER NOT NULL, + "anzahl" INTEGER NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "artikel_name_key" ON "artikel"("name"); diff --git a/api/db/migrations/20231105165906_/migration.sql b/api/db/migrations/20231105165906_/migration.sql new file mode 100644 index 0000000..519a0f3 --- /dev/null +++ b/api/db/migrations/20231105165906_/migration.sql @@ -0,0 +1,49 @@ +/* + Warnings: + + - You are about to drop the `artikel` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `kauf` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `kauf_artikel` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "artikel"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "kauf"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "kauf_artikel"; +PRAGMA foreign_keys=on; + +-- CreateTable +CREATE TABLE "Kauf_Artikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "kauf_id" INTEGER NOT NULL, + "artikel_id" INTEGER NOT NULL, + "anzahl" INTEGER NOT NULL, + CONSTRAINT "Kauf_Artikel_kauf_id_fkey" FOREIGN KEY ("kauf_id") REFERENCES "Kauf" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Kauf_Artikel_artikel_id_fkey" FOREIGN KEY ("artikel_id") REFERENCES "Artikel" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Artikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "preis" REAL +); + +-- CreateTable +CREATE TABLE "Kauf" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "timestamp" INTEGER NOT NULL, + "preis_ges" REAL +); + +-- CreateIndex +CREATE UNIQUE INDEX "Artikel_name_key" ON "Artikel"("name"); diff --git a/api/db/migrations/20231105172818_/migration.sql b/api/db/migrations/20231105172818_/migration.sql new file mode 100644 index 0000000..abd376e --- /dev/null +++ b/api/db/migrations/20231105172818_/migration.sql @@ -0,0 +1,47 @@ +/* + Warnings: + + - You are about to drop the `Kauf_Artikel` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "Kauf_Artikel"; +PRAGMA foreign_keys=on; + +-- CreateTable +CREATE TABLE "KaufArtikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "kauf_id" INTEGER NOT NULL, + "artikel_id" INTEGER NOT NULL, + "anzahl" INTEGER NOT NULL, + CONSTRAINT "KaufArtikel_kauf_id_fkey" FOREIGN KEY ("kauf_id") REFERENCES "Kauf" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "KaufArtikel_artikel_id_fkey" FOREIGN KEY ("artikel_id") REFERENCES "Artikel" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Kategorie" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "timestamp" INTEGER NOT NULL, + "preis_ges" REAL +); + +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Artikel" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "preis" REAL, + "kategorie_id" INTEGER, + CONSTRAINT "Artikel_kategorie_id_fkey" FOREIGN KEY ("kategorie_id") REFERENCES "Kategorie" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_Artikel" ("id", "name", "preis") SELECT "id", "name", "preis" FROM "Artikel"; +DROP TABLE "Artikel"; +ALTER TABLE "new_Artikel" RENAME TO "Artikel"; +CREATE UNIQUE INDEX "Artikel_name_key" ON "Artikel"("name"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; + +-- CreateIndex +CREATE UNIQUE INDEX "Kategorie_name_key" ON "Kategorie"("name"); diff --git a/api/db/migrations/migration_lock.toml b/api/db/migrations/migration_lock.toml new file mode 100644 index 0000000..e5e5c47 --- /dev/null +++ b/api/db/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/api/db/schema.prisma b/api/db/schema.prisma index 3dea71a..4c32482 100644 --- a/api/db/schema.prisma +++ b/api/db/schema.prisma @@ -9,10 +9,34 @@ generator client { } // Define your own datamodels here and run `yarn redwood prisma migrate dev` -// to create migrations for them and apply to your dev DB. -// TODO: Please remove the following example: -model UserExample { - id Int @id @default(autoincrement()) - email String @unique - name String? + +model KaufArtikel { + id Int @id @default(autoincrement()) + kauf Kauf @relation(fields: [kauf_id], references: [id]) + kauf_id Int + artikel Artikel @relation(fields: [artikel_id], references: [id]) + artikel_id Int + anzahl Int +} + +model Artikel { + id Int @id @default(autoincrement()) + name String @unique + preis Float? + kategorie Kategorie? @relation(fields: [kategorie_id], references: [id]) + kategorie_id Int? + kaeufe KaufArtikel[] +} + +model Kategorie { + id Int @id @default(autoincrement()) + name String @unique + artikel Artikel[] +} + +model Kauf { + id Int @id @default(autoincrement()) + timestamp DateTime @default(now()) + preis_ges Float? + artikel KaufArtikel[] } diff --git a/api/src/graphql/artikels.sdl.js b/api/src/graphql/artikels.sdl.js new file mode 100644 index 0000000..7a20f67 --- /dev/null +++ b/api/src/graphql/artikels.sdl.js @@ -0,0 +1,33 @@ +export const schema = gql` + type Artikel { + id: Int! + name: String! + preis: Float + kategorie: Kategorie + kategorie_id: Int + kaeufe: [KaufArtikel]! + } + + type Query { + artikels: [Artikel!]! @requireAuth + artikel(id: Int!): Artikel @requireAuth + } + + input CreateArtikelInput { + name: String! + preis: Float + kategorie_id: Int + } + + input UpdateArtikelInput { + name: String + preis: Float + kategorie_id: Int + } + + type Mutation { + createArtikel(input: CreateArtikelInput!): Artikel! @requireAuth + updateArtikel(id: Int!, input: UpdateArtikelInput!): Artikel! @requireAuth + deleteArtikel(id: Int!): Artikel! @requireAuth + } +` diff --git a/api/src/graphql/kategories.sdl.js b/api/src/graphql/kategories.sdl.js new file mode 100644 index 0000000..93bf6cc --- /dev/null +++ b/api/src/graphql/kategories.sdl.js @@ -0,0 +1,27 @@ +export const schema = gql` + type Kategorie { + id: Int! + name: String! + artikel: [Artikel]! + } + + type Query { + kategories: [Kategorie!]! @requireAuth + kategorie(id: Int!): Kategorie @requireAuth + } + + input CreateKategorieInput { + name: String! + } + + input UpdateKategorieInput { + name: String + } + + type Mutation { + createKategorie(input: CreateKategorieInput!): Kategorie! @requireAuth + updateKategorie(id: Int!, input: UpdateKategorieInput!): Kategorie! + @requireAuth + deleteKategorie(id: Int!): Kategorie! @requireAuth + } +` diff --git a/api/src/graphql/kaufArtikels.sdl.js b/api/src/graphql/kaufArtikels.sdl.js new file mode 100644 index 0000000..733f3c2 --- /dev/null +++ b/api/src/graphql/kaufArtikels.sdl.js @@ -0,0 +1,34 @@ +export const schema = gql` + type KaufArtikel { + id: Int! + kauf: Kauf! + kauf_id: Int! + artikel: Artikel! + artikel_id: Int! + anzahl: Int! + } + + type Query { + kaufArtikels: [KaufArtikel!]! @requireAuth + kaufArtikel(id: Int!): KaufArtikel @requireAuth + } + + input CreateKaufArtikelInput { + kauf_id: Int! + artikel_id: Int! + anzahl: Int! + } + + input UpdateKaufArtikelInput { + kauf_id: Int + artikel_id: Int + anzahl: Int + } + + type Mutation { + createKaufArtikel(input: CreateKaufArtikelInput!): KaufArtikel! @requireAuth + updateKaufArtikel(id: Int!, input: UpdateKaufArtikelInput!): KaufArtikel! + @requireAuth + deleteKaufArtikel(id: Int!): KaufArtikel! @requireAuth + } +` diff --git a/api/src/graphql/kaufs.sdl.js b/api/src/graphql/kaufs.sdl.js new file mode 100644 index 0000000..52cb344 --- /dev/null +++ b/api/src/graphql/kaufs.sdl.js @@ -0,0 +1,29 @@ +export const schema = gql` + type Kauf { + id: Int! + timestamp: DateTime! + preis_ges: Float + artikel: [KaufArtikel]! + } + + type Query { + kaufs: [Kauf!]! @requireAuth + kauf(id: Int!): Kauf @requireAuth + } + + input CreateKaufInput { + timestamp: DateTime! + preis_ges: Float + } + + input UpdateKaufInput { + timestamp: DateTime + preis_ges: Float + } + + type Mutation { + createKauf(input: CreateKaufInput!): Kauf! @requireAuth + updateKauf(id: Int!, input: UpdateKaufInput!): Kauf! @requireAuth + deleteKauf(id: Int!): Kauf! @requireAuth + } +` diff --git a/api/src/services/artikels/artikels.js b/api/src/services/artikels/artikels.js new file mode 100644 index 0000000..bd67349 --- /dev/null +++ b/api/src/services/artikels/artikels.js @@ -0,0 +1,39 @@ +import { db } from 'src/lib/db' + +export const artikels = () => { + return db.artikel.findMany() +} + +export const artikel = ({ id }) => { + return db.artikel.findUnique({ + where: { id }, + }) +} + +export const createArtikel = ({ input }) => { + return db.artikel.create({ + data: input, + }) +} + +export const updateArtikel = ({ id, input }) => { + return db.artikel.update({ + data: input, + where: { id }, + }) +} + +export const deleteArtikel = ({ id }) => { + return db.artikel.delete({ + where: { id }, + }) +} + +export const Artikel = { + kategorie: (_obj, { root }) => { + return db.artikel.findUnique({ where: { id: root?.id } }).kategorie() + }, + kaeufe: (_obj, { root }) => { + return db.artikel.findUnique({ where: { id: root?.id } }).kaeufe() + }, +} diff --git a/api/src/services/artikels/artikels.scenarios.js b/api/src/services/artikels/artikels.scenarios.js new file mode 100644 index 0000000..b87ec55 --- /dev/null +++ b/api/src/services/artikels/artikels.scenarios.js @@ -0,0 +1,6 @@ +export const standard = defineScenario({ + artikel: { + one: { data: { name: 'String735050' } }, + two: { data: { name: 'String6988247' } }, + }, +}) diff --git a/api/src/services/artikels/artikels.test.js b/api/src/services/artikels/artikels.test.js new file mode 100644 index 0000000..3e8aaf6 --- /dev/null +++ b/api/src/services/artikels/artikels.test.js @@ -0,0 +1,54 @@ +import { + artikels, + artikel, + createArtikel, + updateArtikel, + deleteArtikel, +} from './artikels' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-services +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('artikels', () => { + scenario('returns all artikels', async (scenario) => { + const result = await artikels() + + expect(result.length).toEqual(Object.keys(scenario.artikel).length) + }) + + scenario('returns a single artikel', async (scenario) => { + const result = await artikel({ id: scenario.artikel.one.id }) + + expect(result).toEqual(scenario.artikel.one) + }) + + scenario('creates a artikel', async () => { + const result = await createArtikel({ + input: { name: 'String6975650' }, + }) + + expect(result.name).toEqual('String6975650') + }) + + scenario('updates a artikel', async (scenario) => { + const original = await artikel({ id: scenario.artikel.one.id }) + const result = await updateArtikel({ + id: original.id, + input: { name: 'String5200162' }, + }) + + expect(result.name).toEqual('String5200162') + }) + + scenario('deletes a artikel', async (scenario) => { + const original = await deleteArtikel({ + id: scenario.artikel.one.id, + }) + const result = await artikel({ id: original.id }) + + expect(result).toEqual(null) + }) +}) diff --git a/api/src/services/kategories/kategories.js b/api/src/services/kategories/kategories.js new file mode 100644 index 0000000..0373669 --- /dev/null +++ b/api/src/services/kategories/kategories.js @@ -0,0 +1,36 @@ +import { db } from 'src/lib/db' + +export const kategories = () => { + return db.kategorie.findMany() +} + +export const kategorie = ({ id }) => { + return db.kategorie.findUnique({ + where: { id }, + }) +} + +export const createKategorie = ({ input }) => { + return db.kategorie.create({ + data: input, + }) +} + +export const updateKategorie = ({ id, input }) => { + return db.kategorie.update({ + data: input, + where: { id }, + }) +} + +export const deleteKategorie = ({ id }) => { + return db.kategorie.delete({ + where: { id }, + }) +} + +export const Kategorie = { + artikel: (_obj, { root }) => { + return db.kategorie.findUnique({ where: { id: root?.id } }).artikel() + }, +} diff --git a/api/src/services/kategories/kategories.scenarios.js b/api/src/services/kategories/kategories.scenarios.js new file mode 100644 index 0000000..d2c39fb --- /dev/null +++ b/api/src/services/kategories/kategories.scenarios.js @@ -0,0 +1,6 @@ +export const standard = defineScenario({ + kategorie: { + one: { data: { name: 'String8168544' } }, + two: { data: { name: 'String7171026' } }, + }, +}) diff --git a/api/src/services/kategories/kategories.test.js b/api/src/services/kategories/kategories.test.js new file mode 100644 index 0000000..68daf44 --- /dev/null +++ b/api/src/services/kategories/kategories.test.js @@ -0,0 +1,56 @@ +import { + kategories, + kategorie, + createKategorie, + updateKategorie, + deleteKategorie, +} from './kategories' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-services +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('kategories', () => { + scenario('returns all kategories', async (scenario) => { + const result = await kategories() + + expect(result.length).toEqual(Object.keys(scenario.kategorie).length) + }) + + scenario('returns a single kategorie', async (scenario) => { + const result = await kategorie({ id: scenario.kategorie.one.id }) + + expect(result).toEqual(scenario.kategorie.one) + }) + + scenario('creates a kategorie', async () => { + const result = await createKategorie({ + input: { name: 'String2189335' }, + }) + + expect(result.name).toEqual('String2189335') + }) + + scenario('updates a kategorie', async (scenario) => { + const original = await kategorie({ + id: scenario.kategorie.one.id, + }) + const result = await updateKategorie({ + id: original.id, + input: { name: 'String81840662' }, + }) + + expect(result.name).toEqual('String81840662') + }) + + scenario('deletes a kategorie', async (scenario) => { + const original = await deleteKategorie({ + id: scenario.kategorie.one.id, + }) + const result = await kategorie({ id: original.id }) + + expect(result).toEqual(null) + }) +}) diff --git a/api/src/services/kaufArtikels/kaufArtikels.js b/api/src/services/kaufArtikels/kaufArtikels.js new file mode 100644 index 0000000..5773619 --- /dev/null +++ b/api/src/services/kaufArtikels/kaufArtikels.js @@ -0,0 +1,39 @@ +import { db } from 'src/lib/db' + +export const kaufArtikels = () => { + return db.kaufArtikel.findMany() +} + +export const kaufArtikel = ({ id }) => { + return db.kaufArtikel.findUnique({ + where: { id }, + }) +} + +export const createKaufArtikel = ({ input }) => { + return db.kaufArtikel.create({ + data: input, + }) +} + +export const updateKaufArtikel = ({ id, input }) => { + return db.kaufArtikel.update({ + data: input, + where: { id }, + }) +} + +export const deleteKaufArtikel = ({ id }) => { + return db.kaufArtikel.delete({ + where: { id }, + }) +} + +export const KaufArtikel = { + kauf: (_obj, { root }) => { + return db.kaufArtikel.findUnique({ where: { id: root?.id } }).kauf() + }, + artikel: (_obj, { root }) => { + return db.kaufArtikel.findUnique({ where: { id: root?.id } }).artikel() + }, +} diff --git a/api/src/services/kaufArtikels/kaufArtikels.scenarios.js b/api/src/services/kaufArtikels/kaufArtikels.scenarios.js new file mode 100644 index 0000000..685d9a7 --- /dev/null +++ b/api/src/services/kaufArtikels/kaufArtikels.scenarios.js @@ -0,0 +1,18 @@ +export const standard = defineScenario({ + kaufArtikel: { + one: { + data: { + anzahl: 2209529, + kauf: { create: { timestamp: 597422 } }, + artikel: { create: { name: 'String5421835' } }, + }, + }, + two: { + data: { + anzahl: 9755057, + kauf: { create: { timestamp: 9620697 } }, + artikel: { create: { name: 'String8993250' } }, + }, + }, + }, +}) diff --git a/api/src/services/kaufArtikels/kaufArtikels.test.js b/api/src/services/kaufArtikels/kaufArtikels.test.js new file mode 100644 index 0000000..18ae5f3 --- /dev/null +++ b/api/src/services/kaufArtikels/kaufArtikels.test.js @@ -0,0 +1,62 @@ +import { + kaufArtikels, + kaufArtikel, + createKaufArtikel, + updateKaufArtikel, + deleteKaufArtikel, +} from './kaufArtikels' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-services +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('kaufArtikels', () => { + scenario('returns all kaufArtikels', async (scenario) => { + const result = await kaufArtikels() + + expect(result.length).toEqual(Object.keys(scenario.kaufArtikel).length) + }) + + scenario('returns a single kaufArtikel', async (scenario) => { + const result = await kaufArtikel({ id: scenario.kaufArtikel.one.id }) + + expect(result).toEqual(scenario.kaufArtikel.one) + }) + + scenario('creates a kaufArtikel', async (scenario) => { + const result = await createKaufArtikel({ + input: { + kauf_id: scenario.kaufArtikel.two.kauf_id, + artikel_id: scenario.kaufArtikel.two.artikel_id, + anzahl: 8696596, + }, + }) + + expect(result.kauf_id).toEqual(scenario.kaufArtikel.two.kauf_id) + expect(result.artikel_id).toEqual(scenario.kaufArtikel.two.artikel_id) + expect(result.anzahl).toEqual(8696596) + }) + + scenario('updates a kaufArtikel', async (scenario) => { + const original = await kaufArtikel({ + id: scenario.kaufArtikel.one.id, + }) + const result = await updateKaufArtikel({ + id: original.id, + input: { anzahl: 785481 }, + }) + + expect(result.anzahl).toEqual(785481) + }) + + scenario('deletes a kaufArtikel', async (scenario) => { + const original = await deleteKaufArtikel({ + id: scenario.kaufArtikel.one.id, + }) + const result = await kaufArtikel({ id: original.id }) + + expect(result).toEqual(null) + }) +}) diff --git a/api/src/services/kaufs/kaufs.js b/api/src/services/kaufs/kaufs.js new file mode 100644 index 0000000..2e05afc --- /dev/null +++ b/api/src/services/kaufs/kaufs.js @@ -0,0 +1,36 @@ +import { db } from 'src/lib/db' + +export const kaufs = () => { + return db.kauf.findMany() +} + +export const kauf = ({ id }) => { + return db.kauf.findUnique({ + where: { id }, + }) +} + +export const createKauf = ({ input }) => { + return db.kauf.create({ + data: input, + }) +} + +export const updateKauf = ({ id, input }) => { + return db.kauf.update({ + data: input, + where: { id }, + }) +} + +export const deleteKauf = ({ id }) => { + return db.kauf.delete({ + where: { id }, + }) +} + +export const Kauf = { + artikel: (_obj, { root }) => { + return db.kauf.findUnique({ where: { id: root?.id } }).artikel() + }, +} diff --git a/api/src/services/kaufs/kaufs.scenarios.js b/api/src/services/kaufs/kaufs.scenarios.js new file mode 100644 index 0000000..27771fb --- /dev/null +++ b/api/src/services/kaufs/kaufs.scenarios.js @@ -0,0 +1,3 @@ +export const standard = defineScenario({ + kauf: { one: { data: {} }, two: { data: {} } }, +}) diff --git a/api/src/services/kaufs/kaufs.test.js b/api/src/services/kaufs/kaufs.test.js new file mode 100644 index 0000000..cc12a17 --- /dev/null +++ b/api/src/services/kaufs/kaufs.test.js @@ -0,0 +1,28 @@ +import { kaufs, kauf, deleteKauf } from './kaufs' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-services +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('kaufs', () => { + scenario('returns all kaufs', async (scenario) => { + const result = await kaufs() + + expect(result.length).toEqual(Object.keys(scenario.kauf).length) + }) + + scenario('returns a single kauf', async (scenario) => { + const result = await kauf({ id: scenario.kauf.one.id }) + + expect(result).toEqual(scenario.kauf.one) + }) + + scenario('deletes a kauf', async (scenario) => { + const original = await deleteKauf({ id: scenario.kauf.one.id }) + const result = await kauf({ id: original.id }) + + expect(result).toEqual(null) + }) +}) diff --git a/package.json b/package.json index 2e4974a..2f226c2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ ] }, "devDependencies": { - "@redwoodjs/core": "6.3.2" + "@redwoodjs/core": "6.3.2", + "prettier-plugin-tailwindcss": "0.4.1" }, "eslintConfig": { "extends": "@redwoodjs/eslint-config", diff --git a/prettier.config.js b/prettier.config.js index 45058f7..e532314 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -15,4 +15,6 @@ module.exports = { }, }, ], + tailwindConfig: './web/config/tailwind.config.js', + plugins: [require('prettier-plugin-tailwindcss')], } diff --git a/web/config/postcss.config.js b/web/config/postcss.config.js new file mode 100644 index 0000000..ca420ca --- /dev/null +++ b/web/config/postcss.config.js @@ -0,0 +1,8 @@ +const path = require('path') + +module.exports = { + plugins: [ + require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')), + require('autoprefixer'), + ], +} diff --git a/web/config/tailwind.config.js b/web/config/tailwind.config.js new file mode 100644 index 0000000..2716265 --- /dev/null +++ b/web/config/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['src/**/*.{js,jsx,ts,tsx}'], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/web/package.json b/web/package.json index 97b4bca..725bf07 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "@redwoodjs/forms": "6.3.2", "@redwoodjs/router": "6.3.2", "@redwoodjs/web": "6.3.2", + "humanize-string": "2.1.0", "prop-types": "15.8.1", "react": "18.2.0", "react-dom": "18.2.0" @@ -21,6 +22,10 @@ "devDependencies": { "@redwoodjs/vite": "6.3.2", "@types/react": "18.2.14", - "@types/react-dom": "18.2.6" + "@types/react-dom": "18.2.6", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.31", + "postcss-loader": "^7.3.3", + "tailwindcss": "^3.3.5" } } diff --git a/web/src/App.jsx b/web/src/App.jsx index 97fb5e0..5e7beac 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -4,6 +4,7 @@ import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' +import './scaffold.css' import './index.css' const App = () => ( diff --git a/web/src/Routes.jsx b/web/src/Routes.jsx index 7ab756e..a3d234a 100644 --- a/web/src/Routes.jsx +++ b/web/src/Routes.jsx @@ -7,12 +7,35 @@ // 'src/pages/HomePage/HomePage.js' -> HomePage // 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage -import { Router, Route } from '@redwoodjs/router' +import { Set, Router, Route } from '@redwoodjs/router' + +import ScaffoldLayout from 'src/layouts/ScaffoldLayout' +import PosLayout from 'src/layouts/PosLayout' const Routes = () => { return ( - + + + + + + + + + + + + + + + + + + + + + ) diff --git a/web/src/components/.keep b/web/src/components/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/web/src/components/Artikel/Artikel/Artikel.jsx b/web/src/components/Artikel/Artikel/Artikel.jsx new file mode 100644 index 0000000..2dd730b --- /dev/null +++ b/web/src/components/Artikel/Artikel/Artikel.jsx @@ -0,0 +1,80 @@ +import { Link, routes, navigate } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import 'src/lib/formatters' + +const DELETE_ARTIKEL_MUTATION = gql` + mutation DeleteArtikelMutation($id: Int!) { + deleteArtikel(id: $id) { + id + } + } +` + +const Artikel = ({ artikel }) => { + const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, { + onCompleted: () => { + toast.success('Artikel deleted') + navigate(routes.artikels()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete artikel ' + id + '?')) { + deleteArtikel({ variables: { id } }) + } + } + + return ( + <> +
+
+

+ Artikel {artikel.id} Detail +

+
+ + + + + + + + + + + + + + + + + + + +
Id{artikel.id}
Name{artikel.name}
Preis{artikel.preis}
Kategorie{artikel.kategorie.name}
+
+ + + ) +} + +export default Artikel diff --git a/web/src/components/Artikel/ArtikelCell/ArtikelCell.jsx b/web/src/components/Artikel/ArtikelCell/ArtikelCell.jsx new file mode 100644 index 0000000..3738655 --- /dev/null +++ b/web/src/components/Artikel/ArtikelCell/ArtikelCell.jsx @@ -0,0 +1,24 @@ +import Artikel from 'src/components/Artikel/Artikel' + +export const QUERY = gql` + query FindArtikelById($id: Int!) { + artikel: artikel(id: $id) { + id + name + preis + kategorie{name} + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Artikel not found
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ artikel }) => { + return +} diff --git a/web/src/components/Artikel/ArtikelForm/ArtikelForm.jsx b/web/src/components/Artikel/ArtikelForm/ArtikelForm.jsx new file mode 100644 index 0000000..28f3f20 --- /dev/null +++ b/web/src/components/Artikel/ArtikelForm/ArtikelForm.jsx @@ -0,0 +1,102 @@ +import { + Form, + FormError, + FieldError, + Label, + TextField, + NumberField, + SelectField, + Submit, +} from '@redwoodjs/forms' + +const GET_KATEGORIES = gql` + query FindKategories { + kategories { + id + name + } + } +`; + +const ArtikelForm = (props) => { + const onSubmit = (data) => { + data.kategorie_id = parseInt(data.kategorie_id) + props.onSave(data, props?.artikel?.id) + } + + return ( +
+
+ + + + + + + + + + + + + + + + + + {props.kategories.map((kategorie) => ( + + ))} + + + + +
+ + Save + +
+ +
+ ) +} + +export default ArtikelForm diff --git a/web/src/components/Artikel/Artikels/Artikels.jsx b/web/src/components/Artikel/Artikels/Artikels.jsx new file mode 100644 index 0000000..5af494f --- /dev/null +++ b/web/src/components/Artikel/Artikels/Artikels.jsx @@ -0,0 +1,90 @@ +import { Link, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { QUERY } from 'src/components/Artikel/ArtikelsCell' +import { truncate } from 'src/lib/formatters' + +const DELETE_ARTIKEL_MUTATION = gql` + mutation DeleteArtikelMutation($id: Int!) { + deleteArtikel(id: $id) { + id + } + } +` + +const ArtikelsList = ({ artikels }) => { + const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, { + onCompleted: () => { + toast.success('Artikel deleted') + }, + onError: (error) => { + toast.error(error.message) + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete artikel ' + id + '?')) { + deleteArtikel({ variables: { id } }) + } + } + + return ( +
+ + + + + + + + + + + + {artikels.map((artikel) => ( + + + + + + + + ))} + +
IdNamePreisKategorie 
{truncate(artikel.id)}{truncate(artikel.name)}{truncate(artikel.preis)}{truncate(artikel.kategorie.name)} + +
+
+ ) +} + +export default ArtikelsList diff --git a/web/src/components/Artikel/ArtikelsCell/ArtikelsCell.jsx b/web/src/components/Artikel/ArtikelsCell/ArtikelsCell.jsx new file mode 100644 index 0000000..24b85e9 --- /dev/null +++ b/web/src/components/Artikel/ArtikelsCell/ArtikelsCell.jsx @@ -0,0 +1,35 @@ +import { Link, routes } from '@redwoodjs/router' + +import Artikels from 'src/components/Artikel/Artikels' + +export const QUERY = gql` + query FindArtikels { + artikels { + id + name + preis + kategorie {name} + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No artikels yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({artikels }) => { + return +} diff --git a/web/src/components/Artikel/ArtikelsPos/ArtikelsPos.jsx b/web/src/components/Artikel/ArtikelsPos/ArtikelsPos.jsx new file mode 100644 index 0000000..9d5e3bf --- /dev/null +++ b/web/src/components/Artikel/ArtikelsPos/ArtikelsPos.jsx @@ -0,0 +1,103 @@ +import { Link, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { QUERY } from 'src/components/Artikel/ArtikelsCell' +import { truncate } from 'src/lib/formatters' + +const ArtikelsList = ({ artikels }) => { + console.log(artikels) + let listKategoryArtikels = {} + artikels.forEach((artikel) => { + if(!(artikel.kategorie.id in listKategoryArtikels)){ + listKategoryArtikels[artikel.kategorie.id] = {} + listKategoryArtikels[artikel.kategorie.id]["name"] = artikel.kategorie.name + listKategoryArtikels[artikel.kategorie.id]["artikel"] = [] + } + listKategoryArtikels[artikel.kategorie.id]["artikel"].append(artikel) + }) + + + + const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, { + onCompleted: () => { + toast.success('Artikel deleted') + }, + onError: (error) => { + toast.error(error.message) + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete artikel ' + id + '?')) { + deleteArtikel({ variables: { id } }) + } + } + + return ( +
+ + + + + + + + + + + + {listKategoryArtikels.map((kategorieartikel) => (<> +
+ {kategorieartikel.name} +
+
+ {kategorieartikel["artikel"].map((artikel) => ( +
+ + + + + + + ))} + + + ))} + +
IdNamePreisKategorie 
{truncate(artikel.id)}{truncate(artikel.name)}{truncate(artikel.preis)}{truncate(artikel.kategorie.name)} + +
+
+ ) +} + +export default ArtikelsList diff --git a/web/src/components/Artikel/ArtikelsPosCell/ArtikelsPosCell.jsx b/web/src/components/Artikel/ArtikelsPosCell/ArtikelsPosCell.jsx new file mode 100644 index 0000000..5a6e703 --- /dev/null +++ b/web/src/components/Artikel/ArtikelsPosCell/ArtikelsPosCell.jsx @@ -0,0 +1,35 @@ +import { Link, routes } from '@redwoodjs/router' + +import Artikels from 'src/components/Artikel/Artikels' + +export const QUERY = gql` + query FindArtikels { + artikels { + id + name + preis + kategorie {id, name} + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No artikels yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({artikels }) => { + return +} diff --git a/web/src/components/Artikel/EditArtikelCell/EditArtikelCell.jsx b/web/src/components/Artikel/EditArtikelCell/EditArtikelCell.jsx new file mode 100644 index 0000000..89cd593 --- /dev/null +++ b/web/src/components/Artikel/EditArtikelCell/EditArtikelCell.jsx @@ -0,0 +1,75 @@ +import { navigate, routes } from '@redwoodjs/router' + +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import ArtikelForm from 'src/components/Artikel/ArtikelForm' + +export const QUERY = gql` + query EditArtikelById($id: Int!) { + artikel: artikel(id: $id) { + id + name + preis + kategorie_id + }, + kategories: kategories { + id + name + } + } +` +const UPDATE_ARTIKEL_MUTATION = gql` + mutation UpdateArtikelMutation($id: Int!, $input: UpdateArtikelInput!) { + updateArtikel(id: $id, input: $input) { + id + name + preis + kategorie_id + } + } +` + +export const Loading = () =>
Loading...
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ artikel, kategories }) => { + const [updateArtikel, { loading, error }] = useMutation( + UPDATE_ARTIKEL_MUTATION, + { + onCompleted: () => { + toast.success('Artikel updated') + navigate(routes.artikels()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = (input, id) => { + updateArtikel({ variables: { id, input } }) + } + + return ( +
+
+

+ Edit Artikel {artikel?.id} +

+
+
+ +
+
+ ) +} diff --git a/web/src/components/Artikel/NewArtikelCell/NewArtikelCell.jsx b/web/src/components/Artikel/NewArtikelCell/NewArtikelCell.jsx new file mode 100644 index 0000000..e5c42e2 --- /dev/null +++ b/web/src/components/Artikel/NewArtikelCell/NewArtikelCell.jsx @@ -0,0 +1,45 @@ +import { navigate, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import ArtikelForm from 'src/components/Artikel/ArtikelForm' + +const CREATE_ARTIKEL_MUTATION = gql` + mutation CreateArtikelMutation($input: CreateArtikelInput!) { + createArtikel(input: $input) { + id + } + } +` + +const NewArtikel = ({kategories}) => { + const [createArtikel, { loading, error }] = useMutation( + CREATE_ARTIKEL_MUTATION, + { + onCompleted: () => { + toast.success('Artikel created') + navigate(routes.artikels()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = (input) => { + createArtikel({ variables: { input } }) + } + + return ( +
+
+

New Artikel

+
+
+ +
+
+ ) +} + +export default NewArtikel diff --git a/web/src/components/Kategorie/EditKategorieCell/EditKategorieCell.jsx b/web/src/components/Kategorie/EditKategorieCell/EditKategorieCell.jsx new file mode 100644 index 0000000..cc840ad --- /dev/null +++ b/web/src/components/Kategorie/EditKategorieCell/EditKategorieCell.jsx @@ -0,0 +1,66 @@ +import { navigate, routes } from '@redwoodjs/router' + +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import KategorieForm from 'src/components/Kategorie/KategorieForm' + +export const QUERY = gql` + query EditKategorieById($id: Int!) { + kategorie: kategorie(id: $id) { + id + name + } + } +` +const UPDATE_KATEGORIE_MUTATION = gql` + mutation UpdateKategorieMutation($id: Int!, $input: UpdateKategorieInput!) { + updateKategorie(id: $id, input: $input) { + id + name + } + } +` + +export const Loading = () =>
Loading...
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kategorie }) => { + const [updateKategorie, { loading, error }] = useMutation( + UPDATE_KATEGORIE_MUTATION, + { + onCompleted: () => { + toast.success('Kategorie updated') + navigate(routes.kategories()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = (input, id) => { + updateKategorie({ variables: { id, input } }) + } + + return ( +
+
+

+ Edit Kategorie {kategorie?.id} +

+
+
+ +
+
+ ) +} diff --git a/web/src/components/Kategorie/Kategorie/Kategorie.jsx b/web/src/components/Kategorie/Kategorie/Kategorie.jsx new file mode 100644 index 0000000..070584c --- /dev/null +++ b/web/src/components/Kategorie/Kategorie/Kategorie.jsx @@ -0,0 +1,72 @@ +import { Link, routes, navigate } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import 'src/lib/formatters' + +const DELETE_KATEGORIE_MUTATION = gql` + mutation DeleteKategorieMutation($id: Int!) { + deleteKategorie(id: $id) { + id + } + } +` + +const Kategorie = ({ kategorie }) => { + const [deleteKategorie] = useMutation(DELETE_KATEGORIE_MUTATION, { + onCompleted: () => { + toast.success('Kategorie deleted') + navigate(routes.kategories()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete kategorie ' + id + '?')) { + deleteKategorie({ variables: { id } }) + } + } + + return ( + <> +
+
+

+ Kategorie {kategorie.id} Detail +

+
+ + + + + + + + + + + +
Id{kategorie.id}
Name{kategorie.name}
+
+ + + ) +} + +export default Kategorie diff --git a/web/src/components/Kategorie/KategorieCell/KategorieCell.jsx b/web/src/components/Kategorie/KategorieCell/KategorieCell.jsx new file mode 100644 index 0000000..c109039 --- /dev/null +++ b/web/src/components/Kategorie/KategorieCell/KategorieCell.jsx @@ -0,0 +1,22 @@ +import Kategorie from 'src/components/Kategorie/Kategorie' + +export const QUERY = gql` + query FindKategorieById($id: Int!) { + kategorie: kategorie(id: $id) { + id + name + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Kategorie not found
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kategorie }) => { + return +} diff --git a/web/src/components/Kategorie/KategorieForm/KategorieForm.jsx b/web/src/components/Kategorie/KategorieForm/KategorieForm.jsx new file mode 100644 index 0000000..6485f49 --- /dev/null +++ b/web/src/components/Kategorie/KategorieForm/KategorieForm.jsx @@ -0,0 +1,53 @@ +import { + Form, + FormError, + FieldError, + Label, + TextField, + Submit, +} from '@redwoodjs/forms' + +const KategorieForm = (props) => { + const onSubmit = (data) => { + props.onSave(data, props?.kategorie?.id) + } + + return ( +
+
+ + + + + + + + +
+ + Save + +
+ +
+ ) +} + +export default KategorieForm diff --git a/web/src/components/Kategorie/KategorieSelect/KategoriesSelectCell.jsx b/web/src/components/Kategorie/KategorieSelect/KategoriesSelectCell.jsx new file mode 100644 index 0000000..0042b5f --- /dev/null +++ b/web/src/components/Kategorie/KategorieSelect/KategoriesSelectCell.jsx @@ -0,0 +1,44 @@ +import { Link, routes } from '@redwoodjs/router' + +export const QUERY = gql` + query FindKategories { + kategories { + id + name + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No kategories yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kategories }) => { + return +} + +const KategoriesSelect = ({ kategories, name }) => { + + return ( + + ) +} + +export default KategoriesSelect diff --git a/web/src/components/Kategorie/Kategories/Kategories.jsx b/web/src/components/Kategorie/Kategories/Kategories.jsx new file mode 100644 index 0000000..51ea338 --- /dev/null +++ b/web/src/components/Kategorie/Kategories/Kategories.jsx @@ -0,0 +1,86 @@ +import { Link, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { QUERY } from 'src/components/Kategorie/KategoriesCell' +import { truncate } from 'src/lib/formatters' + +const DELETE_KATEGORIE_MUTATION = gql` + mutation DeleteKategorieMutation($id: Int!) { + deleteKategorie(id: $id) { + id + } + } +` + +const KategoriesList = ({ kategories }) => { + const [deleteKategorie] = useMutation(DELETE_KATEGORIE_MUTATION, { + onCompleted: () => { + toast.success('Kategorie deleted') + }, + onError: (error) => { + toast.error(error.message) + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete kategorie ' + id + '?')) { + deleteKategorie({ variables: { id } }) + } + } + + return ( +
+ + + + + + + + + + {kategories.map((kategorie) => ( + + + + + + ))} + +
IdName 
{truncate(kategorie.id)}{truncate(kategorie.name)} + +
+
+ ) +} + +export default KategoriesList diff --git a/web/src/components/Kategorie/KategoriesCell/KategoriesCell.jsx b/web/src/components/Kategorie/KategoriesCell/KategoriesCell.jsx new file mode 100644 index 0000000..0d5c0a0 --- /dev/null +++ b/web/src/components/Kategorie/KategoriesCell/KategoriesCell.jsx @@ -0,0 +1,33 @@ +import { Link, routes } from '@redwoodjs/router' + +import Kategories from 'src/components/Kategorie/Kategories' + +export const QUERY = gql` + query FindKategories { + kategories { + id + name + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No kategories yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kategories }) => { + return +} diff --git a/web/src/components/Kategorie/NewKategorie/NewKategorie.jsx b/web/src/components/Kategorie/NewKategorie/NewKategorie.jsx new file mode 100644 index 0000000..3b50cbc --- /dev/null +++ b/web/src/components/Kategorie/NewKategorie/NewKategorie.jsx @@ -0,0 +1,45 @@ +import { navigate, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import KategorieForm from 'src/components/Kategorie/KategorieForm' + +const CREATE_KATEGORIE_MUTATION = gql` + mutation CreateKategorieMutation($input: CreateKategorieInput!) { + createKategorie(input: $input) { + id + } + } +` + +const NewKategorie = () => { + const [createKategorie, { loading, error }] = useMutation( + CREATE_KATEGORIE_MUTATION, + { + onCompleted: () => { + toast.success('Kategorie created') + navigate(routes.kategories()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = (input) => { + createKategorie({ variables: { input } }) + } + + return ( +
+
+

New Kategorie

+
+
+ +
+
+ ) +} + +export default NewKategorie diff --git a/web/src/components/Kauf/EditKaufCell/EditKaufCell.jsx b/web/src/components/Kauf/EditKaufCell/EditKaufCell.jsx new file mode 100644 index 0000000..e9ef128 --- /dev/null +++ b/web/src/components/Kauf/EditKaufCell/EditKaufCell.jsx @@ -0,0 +1,60 @@ +import { navigate, routes } from '@redwoodjs/router' + +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import KaufForm from 'src/components/Kauf/KaufForm' + +export const QUERY = gql` + query EditKaufById($id: Int!) { + kauf: kauf(id: $id) { + id + timestamp + preis_ges + } + } +` +const UPDATE_KAUF_MUTATION = gql` + mutation UpdateKaufMutation($id: Int!, $input: UpdateKaufInput!) { + updateKauf(id: $id, input: $input) { + id + timestamp + preis_ges + } + } +` + +export const Loading = () =>
Loading...
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kauf }) => { + const [updateKauf, { loading, error }] = useMutation(UPDATE_KAUF_MUTATION, { + onCompleted: () => { + toast.success('Kauf updated') + navigate(routes.kaufs()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onSave = (input, id) => { + updateKauf({ variables: { id, input } }) + } + + return ( +
+
+

+ Edit Kauf {kauf?.id} +

+
+
+ +
+
+ ) +} diff --git a/web/src/components/Kauf/Kauf/Kauf.jsx b/web/src/components/Kauf/Kauf/Kauf.jsx new file mode 100644 index 0000000..22de63c --- /dev/null +++ b/web/src/components/Kauf/Kauf/Kauf.jsx @@ -0,0 +1,76 @@ +import { Link, routes, navigate } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { timeTag } from 'src/lib/formatters' + +const DELETE_KAUF_MUTATION = gql` + mutation DeleteKaufMutation($id: Int!) { + deleteKauf(id: $id) { + id + } + } +` + +const Kauf = ({ kauf }) => { + const [deleteKauf] = useMutation(DELETE_KAUF_MUTATION, { + onCompleted: () => { + toast.success('Kauf deleted') + navigate(routes.kaufs()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onDeleteClick = (id) => { + if (confirm('Are you sure you want to delete kauf ' + id + '?')) { + deleteKauf({ variables: { id } }) + } + } + + return ( + <> +
+
+

+ Kauf {kauf.id} Detail +

+
+ + + + + + + + + + + + + + + +
Id{kauf.id}
Timestamp{timeTag(kauf.timestamp)}
Preis ges{kauf.preis_ges}
+
+ + + ) +} + +export default Kauf diff --git a/web/src/components/Kauf/KaufCell/KaufCell.jsx b/web/src/components/Kauf/KaufCell/KaufCell.jsx new file mode 100644 index 0000000..0e4b35a --- /dev/null +++ b/web/src/components/Kauf/KaufCell/KaufCell.jsx @@ -0,0 +1,23 @@ +import Kauf from 'src/components/Kauf/Kauf' + +export const QUERY = gql` + query FindKaufById($id: Int!) { + kauf: kauf(id: $id) { + id + timestamp + preis_ges + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Kauf not found
+ +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kauf }) => { + return +} diff --git a/web/src/components/Kauf/KaufForm/KaufForm.jsx b/web/src/components/Kauf/KaufForm/KaufForm.jsx new file mode 100644 index 0000000..eb4dd79 --- /dev/null +++ b/web/src/components/Kauf/KaufForm/KaufForm.jsx @@ -0,0 +1,53 @@ +import { + Form, + FormError, + FieldError, + Label, + TextField, + Submit, +} from '@redwoodjs/forms' + +const KaufForm = (props) => { + const onSubmit = (data) => { + props.onSave(data, props?.kauf?.id) + } + + return ( +
+
+ + + + + + + + +
+ + Save + +
+ +
+ ) +} + +export default KaufForm diff --git a/web/src/components/Kauf/Kaufs/Kaufs.jsx b/web/src/components/Kauf/Kaufs/Kaufs.jsx new file mode 100644 index 0000000..9a9526e --- /dev/null +++ b/web/src/components/Kauf/Kaufs/Kaufs.jsx @@ -0,0 +1,27 @@ +import { timeTag, truncate } from 'src/lib/formatters' + +const KaufsList = ({ kaufs }) => { + + return ( +
+ + + + + + + + + {kaufs.map((kauf) => ( + + + + + ))} + +
IdUhrzeit
{truncate(kauf.id+100)}{truncate(kauf.timestamp)}
+
+ ) +} + +export default KaufsList diff --git a/web/src/components/Kauf/KaufsCell/KaufsCell.jsx b/web/src/components/Kauf/KaufsCell/KaufsCell.jsx new file mode 100644 index 0000000..9323131 --- /dev/null +++ b/web/src/components/Kauf/KaufsCell/KaufsCell.jsx @@ -0,0 +1,34 @@ +import { Link, routes } from '@redwoodjs/router' + +import Kaufs from 'src/components/Kauf/Kaufs' + +export const QUERY = gql` + query FindKaufs { + kaufs { + id + timestamp + preis_ges + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No kaufs yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kaufs }) => { + return +} diff --git a/web/src/components/Kauf/KaufsPosCell/KaufsPosCell.jsx b/web/src/components/Kauf/KaufsPosCell/KaufsPosCell.jsx new file mode 100644 index 0000000..51d3e35 --- /dev/null +++ b/web/src/components/Kauf/KaufsPosCell/KaufsPosCell.jsx @@ -0,0 +1,60 @@ +import { Link, routes } from '@redwoodjs/router' + +export const QUERY = gql` + query FindKaufs { + kaufs { + id + timestamp + preis_ges + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No kaufs yet. '} +
+ ) +} + +export const Failure = ({ error }) => ( +
{error?.message}
+) + +export const Success = ({ kaufs }) => { + return (
+ + + + + + + + + + + {kaufs.map((kauf) => ( + + + + + + + ))} + +
IdTimestampPreis ges 
{truncate(kauf.id+100)}{timeTag(kauf.timestamp)}{truncate(kauf.preis_ges)} + +
+
) +} \ No newline at end of file diff --git a/web/src/components/Kauf/NewKauf/NewKauf.jsx b/web/src/components/Kauf/NewKauf/NewKauf.jsx new file mode 100644 index 0000000..82d80e7 --- /dev/null +++ b/web/src/components/Kauf/NewKauf/NewKauf.jsx @@ -0,0 +1,42 @@ +import { navigate, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import KaufForm from 'src/components/Kauf/KaufForm' + +const CREATE_KAUF_MUTATION = gql` + mutation CreateKaufMutation($input: CreateKaufInput!) { + createKauf(input: $input) { + id + } + } +` + +const NewKauf = () => { + const [createKauf, { loading, error }] = useMutation(CREATE_KAUF_MUTATION, { + onCompleted: () => { + toast.success('Kauf created') + navigate(routes.kaufs()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onSave = (input) => { + createKauf({ variables: { input } }) + } + + return ( +
+
+

New Kauf

+
+
+ +
+
+ ) +} + +export default NewKauf diff --git a/web/src/index.css b/web/src/index.css index e69de29..b31cb33 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -0,0 +1,13 @@ +/** + * START --- SETUP TAILWINDCSS EDIT + * + * `yarn rw setup ui tailwindcss` placed these directives here + * to inject Tailwind's styles into your CSS. + * For more information, see: https://tailwindcss.com/docs/installation + */ +@tailwind base; +@tailwind components; +@tailwind utilities; +/** + * END --- SETUP TAILWINDCSS EDIT + */ diff --git a/web/src/layouts/PosLayout/PosLayout.jsx b/web/src/layouts/PosLayout/PosLayout.jsx new file mode 100644 index 0000000..2143496 --- /dev/null +++ b/web/src/layouts/PosLayout/PosLayout.jsx @@ -0,0 +1,26 @@ +import { Link, routes } from '@redwoodjs/router' +import { Toaster } from '@redwoodjs/web/toast' + +const ScaffoldLayout = ({ + title, + titleTo, + buttonLabel, + buttonTo, + children, +}) => { + return ( +
+ +
+

+ + {title} + +

+
+
{children}
+
+ ) +} + +export default ScaffoldLayout diff --git a/web/src/layouts/PosLayout/ScaffoldLayout.jsx b/web/src/layouts/PosLayout/ScaffoldLayout.jsx new file mode 100644 index 0000000..2143496 --- /dev/null +++ b/web/src/layouts/PosLayout/ScaffoldLayout.jsx @@ -0,0 +1,26 @@ +import { Link, routes } from '@redwoodjs/router' +import { Toaster } from '@redwoodjs/web/toast' + +const ScaffoldLayout = ({ + title, + titleTo, + buttonLabel, + buttonTo, + children, +}) => { + return ( +
+ +
+

+ + {title} + +

+
+
{children}
+
+ ) +} + +export default ScaffoldLayout diff --git a/web/src/layouts/ScaffoldLayout/ScaffoldLayout.jsx b/web/src/layouts/ScaffoldLayout/ScaffoldLayout.jsx new file mode 100644 index 0000000..fc2fd85 --- /dev/null +++ b/web/src/layouts/ScaffoldLayout/ScaffoldLayout.jsx @@ -0,0 +1,29 @@ +import { Link, routes } from '@redwoodjs/router' +import { Toaster } from '@redwoodjs/web/toast' + +const ScaffoldLayout = ({ + title, + titleTo, + buttonLabel, + buttonTo, + children, +}) => { + return ( +
+ +
+

+ + {title} + +

+ +
+
{buttonLabel} + +
+
{children}
+
+ ) +} + +export default ScaffoldLayout diff --git a/web/src/lib/formatters.jsx b/web/src/lib/formatters.jsx new file mode 100644 index 0000000..cb4051f --- /dev/null +++ b/web/src/lib/formatters.jsx @@ -0,0 +1,58 @@ +import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values) => { + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (typeof values === 'string') { + output = humanize(values) + } + + return output +} + +export const jsonDisplay = (obj) => { + return ( +
+      {JSON.stringify(obj, null, 2)}
+    
+ ) +} + +export const truncate = (value) => { + let output = value?.toString() ?? '' + + if (output.length > MAX_STRING_LENGTH) { + output = output.substring(0, MAX_STRING_LENGTH) + '...' + } + + return output +} + +export const jsonTruncate = (obj) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (dateTime) => { + let output = '' + + if (dateTime) { + output = ( + + ) + } + + return output +} + +export const checkboxInputTag = (checked) => { + return +} diff --git a/web/src/lib/formatters.test.jsx b/web/src/lib/formatters.test.jsx new file mode 100644 index 0000000..5659338 --- /dev/null +++ b/web/src/lib/formatters.test.jsx @@ -0,0 +1,192 @@ +import { render, waitFor, screen } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + jsonDisplay, + checkboxInputTag, +} from './formatters' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\w\.\.\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\n.+\w\.\.\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + expect(timeTag('')).toEqual('') + }) +}) + +describe('jsonDisplay', () => { + it('produces the correct output', () => { + expect( + jsonDisplay({ + title: 'TOML Example (but in JSON)', + database: { + data: [['delta', 'phi'], [3.14]], + enabled: true, + ports: [8000, 8001, 8002], + temp_targets: { + case: 72.0, + cpu: 79.5, + }, + }, + owner: { + dob: '1979-05-27T07:32:00-08:00', + name: 'Tom Preston-Werner', + }, + servers: { + alpha: { + ip: '10.0.0.1', + role: 'frontend', + }, + beta: { + ip: '10.0.0.2', + role: 'backend', + }, + }, + }) + ).toMatchInlineSnapshot(` +
+        
+          {
+        "title": "TOML Example (but in JSON)",
+        "database": {
+          "data": [
+            [
+              "delta",
+              "phi"
+            ],
+            [
+              3.14
+            ]
+          ],
+          "enabled": true,
+          "ports": [
+            8000,
+            8001,
+            8002
+          ],
+          "temp_targets": {
+            "case": 72,
+            "cpu": 79.5
+          }
+        },
+        "owner": {
+          "dob": "1979-05-27T07:32:00-08:00",
+          "name": "Tom Preston-Werner"
+        },
+        "servers": {
+          "alpha": {
+            "ip": "10.0.0.1",
+            "role": "frontend"
+          },
+          "beta": {
+            "ip": "10.0.0.2",
+            "role": "backend"
+          }
+        }
+      }
+        
+      
+ `) + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) diff --git a/web/src/pages/Artikel/ArtikelPage/ArtikelPage.jsx b/web/src/pages/Artikel/ArtikelPage/ArtikelPage.jsx new file mode 100644 index 0000000..61ca30f --- /dev/null +++ b/web/src/pages/Artikel/ArtikelPage/ArtikelPage.jsx @@ -0,0 +1,7 @@ +import ArtikelCell from 'src/components/Artikel/ArtikelCell' + +const ArtikelPage = ({ id }) => { + return +} + +export default ArtikelPage diff --git a/web/src/pages/Artikel/ArtikelsPage/ArtikelsPage.jsx b/web/src/pages/Artikel/ArtikelsPage/ArtikelsPage.jsx new file mode 100644 index 0000000..7e67f55 --- /dev/null +++ b/web/src/pages/Artikel/ArtikelsPage/ArtikelsPage.jsx @@ -0,0 +1,7 @@ +import ArtikelsCell from 'src/components/Artikel/ArtikelsCell' + +const ArtikelsPage = () => { + return +} + +export default ArtikelsPage diff --git a/web/src/pages/Artikel/EditArtikelPage/EditArtikelPage.jsx b/web/src/pages/Artikel/EditArtikelPage/EditArtikelPage.jsx new file mode 100644 index 0000000..3d14ae0 --- /dev/null +++ b/web/src/pages/Artikel/EditArtikelPage/EditArtikelPage.jsx @@ -0,0 +1,7 @@ +import EditArtikelCell from 'src/components/Artikel/EditArtikelCell' + +const EditArtikelPage = ({ id }) => { + return +} + +export default EditArtikelPage diff --git a/web/src/pages/Artikel/NewArtikelPage/NewArtikelPage.jsx b/web/src/pages/Artikel/NewArtikelPage/NewArtikelPage.jsx new file mode 100644 index 0000000..80d4973 --- /dev/null +++ b/web/src/pages/Artikel/NewArtikelPage/NewArtikelPage.jsx @@ -0,0 +1,7 @@ +import NewArtikel from 'src/components/Artikel/NewArtikelCell' + +const NewArtikelPage = () => { + return +} + +export default NewArtikelPage diff --git a/web/src/pages/Kategorie/EditKategoriePage/EditKategoriePage.jsx b/web/src/pages/Kategorie/EditKategoriePage/EditKategoriePage.jsx new file mode 100644 index 0000000..f9ac47e --- /dev/null +++ b/web/src/pages/Kategorie/EditKategoriePage/EditKategoriePage.jsx @@ -0,0 +1,7 @@ +import EditKategorieCell from 'src/components/Kategorie/EditKategorieCell' + +const EditKategoriePage = ({ id }) => { + return +} + +export default EditKategoriePage diff --git a/web/src/pages/Kategorie/KategoriePage/KategoriePage.jsx b/web/src/pages/Kategorie/KategoriePage/KategoriePage.jsx new file mode 100644 index 0000000..5f8df60 --- /dev/null +++ b/web/src/pages/Kategorie/KategoriePage/KategoriePage.jsx @@ -0,0 +1,7 @@ +import KategorieCell from 'src/components/Kategorie/KategorieCell' + +const KategoriePage = ({ id }) => { + return +} + +export default KategoriePage diff --git a/web/src/pages/Kategorie/KategoriesPage/KategoriesPage.jsx b/web/src/pages/Kategorie/KategoriesPage/KategoriesPage.jsx new file mode 100644 index 0000000..9362624 --- /dev/null +++ b/web/src/pages/Kategorie/KategoriesPage/KategoriesPage.jsx @@ -0,0 +1,7 @@ +import KategoriesCell from 'src/components/Kategorie/KategoriesCell' + +const KategoriesPage = () => { + return +} + +export default KategoriesPage diff --git a/web/src/pages/Kategorie/NewKategoriePage/NewKategoriePage.jsx b/web/src/pages/Kategorie/NewKategoriePage/NewKategoriePage.jsx new file mode 100644 index 0000000..05e3487 --- /dev/null +++ b/web/src/pages/Kategorie/NewKategoriePage/NewKategoriePage.jsx @@ -0,0 +1,7 @@ +import NewKategorie from 'src/components/Kategorie/NewKategorie' + +const NewKategoriePage = () => { + return +} + +export default NewKategoriePage diff --git a/web/src/pages/Kauf/EditKaufPage/EditKaufPage.jsx b/web/src/pages/Kauf/EditKaufPage/EditKaufPage.jsx new file mode 100644 index 0000000..ef70b2f --- /dev/null +++ b/web/src/pages/Kauf/EditKaufPage/EditKaufPage.jsx @@ -0,0 +1,7 @@ +import EditKaufCell from 'src/components/Kauf/EditKaufCell' + +const EditKaufPage = ({ id }) => { + return +} + +export default EditKaufPage diff --git a/web/src/pages/Kauf/KaufPage/KaufPage.jsx b/web/src/pages/Kauf/KaufPage/KaufPage.jsx new file mode 100644 index 0000000..3aa2cab --- /dev/null +++ b/web/src/pages/Kauf/KaufPage/KaufPage.jsx @@ -0,0 +1,7 @@ +import KaufCell from 'src/components/Kauf/KaufCell' + +const KaufPage = ({ id }) => { + return +} + +export default KaufPage diff --git a/web/src/pages/Kauf/KaufsPage/KaufsPage.jsx b/web/src/pages/Kauf/KaufsPage/KaufsPage.jsx new file mode 100644 index 0000000..d487cde --- /dev/null +++ b/web/src/pages/Kauf/KaufsPage/KaufsPage.jsx @@ -0,0 +1,7 @@ +import KaufsCell from 'src/components/Kauf/KaufsCell' + +const KaufsPage = () => { + return +} + +export default KaufsPage diff --git a/web/src/pages/Kauf/NewKaufPage/NewKaufPage.jsx b/web/src/pages/Kauf/NewKaufPage/NewKaufPage.jsx new file mode 100644 index 0000000..76949b9 --- /dev/null +++ b/web/src/pages/Kauf/NewKaufPage/NewKaufPage.jsx @@ -0,0 +1,7 @@ +import NewKauf from 'src/components/Kauf/NewKauf' + +const NewKaufPage = () => { + return +} + +export default NewKaufPage diff --git a/web/src/pages/PosPage/PosPage.jsx b/web/src/pages/PosPage/PosPage.jsx index 43f3224..81ab29d 100644 --- a/web/src/pages/PosPage/PosPage.jsx +++ b/web/src/pages/PosPage/PosPage.jsx @@ -1,19 +1,22 @@ import { Link, routes } from '@redwoodjs/router' import { MetaTags } from '@redwoodjs/web' - +import KaufsPosCell from 'src/components/Kauf/KaufsPosCell' +import ArtikelsPos from 'src/components/Artikel/ArtikelsPos' const PosPage = () => { return ( <> +
+
+
+ +
+
+ +
+
+
-

PosPage

-

- Find me in ./web/src/pages/PosPage/PosPage.jsx -

-

- My default route is named pos, link to me with ` - Pos` -

) } diff --git a/web/src/scaffold.css b/web/src/scaffold.css new file mode 100644 index 0000000..3a6a215 --- /dev/null +++ b/web/src/scaffold.css @@ -0,0 +1,397 @@ +/* + normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css +*/ + +.rw-scaffold *, +.rw-scaffold ::after, +.rw-scaffold ::before { + box-sizing: inherit; +} +.rw-scaffold main { + color: #4a5568; + display: block; +} +.rw-scaffold h1, +.rw-scaffold h2 { + margin: 0; +} +.rw-scaffold a { + background-color: transparent; +} +.rw-scaffold ul { + margin: 0; + padding: 0; +} +.rw-scaffold input { + font-family: inherit; + font-size: 100%; + overflow: visible; +} +.rw-scaffold input:-ms-input-placeholder { + color: #a0aec0; +} +.rw-scaffold input::-ms-input-placeholder { + color: #a0aec0; +} +.rw-scaffold input::placeholder { + color: #a0aec0; +} +.rw-scaffold table { + border-collapse: collapse; +} + +/* + Style +*/ + +.rw-scaffold, +.rw-toast { + background-color: #fff; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} +.rw-header { + display: flex; + justify-content: space-between; + padding: 1rem 2rem 1rem 2rem; +} +.rw-main { + margin-left: 1rem; + margin-right: 1rem; + padding-bottom: 1rem; +} +.rw-segment { + border-radius: 0.5rem; + border-width: 1px; + border-color: #e5e7eb; + overflow: hidden; + width: 100%; + scrollbar-color: #a1a1aa transparent; +} +.rw-segment::-webkit-scrollbar { + height: initial; +} +.rw-segment::-webkit-scrollbar-track { + background-color: transparent; + border-color: #e2e8f0; + border-style: solid; + border-radius: 0 0 10px 10px; + border-width: 1px 0 0 0; + padding: 2px; +} +.rw-segment::-webkit-scrollbar-thumb { + background-color: #a1a1aa; + background-clip: content-box; + border: 3px solid transparent; + border-radius: 10px; +} +.rw-segment-header { + background-color: #e2e8f0; + color: #4a5568; + padding: 0.75rem 1rem; +} +.rw-segment-main { + background-color: #f7fafc; + padding: 1rem; +} +.rw-link { + color: #4299e1; + text-decoration: underline; +} +.rw-link:hover { + color: #2b6cb0; +} +.rw-forgot-link { + font-size: 0.75rem; + color: #a0aec0; + text-align: right; + margin-top: 0.1rem; +} +.rw-forgot-link:hover { + font-size: 0.75rem; + color: #4299e1; +} +.rw-heading { + font-weight: 600; +} +.rw-heading.rw-heading-primary { + font-size: 1.25rem; +} +.rw-heading.rw-heading-secondary { + font-size: 0.875rem; +} +.rw-heading .rw-link { + color: #4a5568; + text-decoration: none; +} +.rw-heading .rw-link:hover { + color: #1a202c; + text-decoration: underline; +} +.rw-cell-error { + font-size: 90%; + font-weight: 600; +} +.rw-form-wrapper { + box-sizing: border-box; + font-size: 0.875rem; + margin-top: -1rem; +} +.rw-cell-error, +.rw-form-error-wrapper { + padding: 1rem; + background-color: #fff5f5; + color: #c53030; + border-width: 1px; + border-color: #feb2b2; + border-radius: 0.25rem; + margin: 1rem 0; +} +.rw-form-error-title { + margin-top: 0; + margin-bottom: 0; + font-weight: 600; +} +.rw-form-error-list { + margin-top: 0.5rem; + list-style-type: disc; + list-style-position: inside; +} +.rw-button { + border: none; + color: #718096; + cursor: pointer; + display: flex; + justify-content: center; + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 1rem; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 0.025em; + border-radius: 0.25rem; + line-height: 2; + border: 0; +} +.rw-button:hover { + background-color: #718096; + color: #fff; +} +.rw-button.rw-button-small { + font-size: 0.75rem; + border-radius: 0.125rem; + padding: 0.25rem 0.5rem; + line-height: inherit; +} +.rw-button.rw-button-green { + background-color: #48bb78; + color: #fff; +} +.rw-button.rw-button-green:hover { + background-color: #38a169; + color: #fff; +} +.rw-button.rw-button-blue { + background-color: #3182ce; + color: #fff; +} +.rw-button.rw-button-blue:hover { + background-color: #2b6cb0; +} +.rw-button.rw-button-red { + background-color: #e53e3e; + color: #fff; +} +.rw-button.rw-button-red:hover { + background-color: #c53030; +} +.rw-button-icon { + font-size: 1.25rem; + line-height: 1; + margin-right: 0.25rem; +} +.rw-button-group { + display: flex; + justify-content: center; + margin: 0.75rem 0.5rem; +} +.rw-button-group .rw-button { + margin: 0 0.25rem; +} +.rw-form-wrapper .rw-button-group { + margin-top: 2rem; + margin-bottom: 0; +} +.rw-label { + display: block; + margin-top: 1.5rem; + color: #4a5568; + font-weight: 600; + text-align: left; +} +.rw-label.rw-label-error { + color: #c53030; +} +.rw-input { + display: block; + margin-top: 0.5rem; + width: 100%; + padding: 0.5rem; + border-width: 1px; + border-style: solid; + border-color: #e2e8f0; + color: #4a5568; + border-radius: 0.25rem; + outline: none; +} +.rw-check-radio-item-none { + color: #4a5568; +} +.rw-check-radio-items { + display: flex; + justify-items: center; +} +.rw-input[type='checkbox'] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; +} +.rw-input[type='radio'] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; +} +.rw-input:focus { + border-color: #a0aec0; +} +.rw-input-error { + border-color: #c53030; + color: #c53030; +} + +.rw-input-error:focus { + outline: none; + border-color: #c53030; + box-shadow: 0 0 5px #c53030; +} + +.rw-field-error { + display: block; + margin-top: 0.25rem; + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; + color: #c53030; +} +.rw-table-wrapper-responsive { + overflow-x: auto; +} +.rw-table-wrapper-responsive .rw-table { + min-width: 48rem; +} +.rw-table { + table-layout: auto; + width: 100%; + font-size: 0.875rem; +} +.rw-table th, +.rw-table td { + padding: 0.75rem; +} +.rw-table td { + background-color: #ffffff; + color: #1a202c; +} +.rw-table tr:nth-child(odd) td, +.rw-table tr:nth-child(odd) th { + background-color: #f7fafc; +} +.rw-table thead tr { + color: #4a5568; +} +.rw-table th { + font-weight: 600; + text-align: left; +} +.rw-table thead th { + background-color: #e2e8f0; + text-align: left; +} +.rw-table tbody th { + text-align: right; +} +@media (min-width: 768px) { + .rw-table tbody th { + width: 20%; + } +} +.rw-table tbody tr { + border-top-width: 1px; +} +.rw-table input { + margin-left: 0; +} +.rw-table-actions { + display: flex; + justify-content: flex-end; + align-items: center; + height: 17px; + padding-right: 0.25rem; +} +.rw-table-actions .rw-button { + background-color: transparent; +} +.rw-table-actions .rw-button:hover { + background-color: #718096; + color: #fff; +} +.rw-table-actions .rw-button-blue { + color: #3182ce; +} +.rw-table-actions .rw-button-blue:hover { + background-color: #3182ce; + color: #fff; +} +.rw-table-actions .rw-button-red { + color: #e53e3e; +} +.rw-table-actions .rw-button-red:hover { + background-color: #e53e3e; + color: #fff; +} +.rw-text-center { + text-align: center; +} +.rw-login-container { + display: flex; + align-items: center; + justify-content: center; + width: 24rem; + margin: 4rem auto; + flex-wrap: wrap; +} +.rw-login-container .rw-form-wrapper { + width: 100%; + text-align: center; +} +.rw-login-link { + margin-top: 1rem; + color: #4a5568; + font-size: 90%; + text-align: center; + flex-basis: 100%; +} +.rw-webauthn-wrapper { + margin: 1.5rem 1rem 1rem; + line-height: 1.4; +} +.rw-webauthn-wrapper h2 { + font-size: 150%; + font-weight: bold; + margin-bottom: 1rem; +} diff --git a/yarn.lock b/yarn.lock index 265976f..0e5ecb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,6 +19,13 @@ __metadata: languageName: node linkType: hard +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: 7b878c48b9d25277d0e1a9b8b2f2312a314af806b4129dc902f2bc29ab09b58236e53964689feec187b28c80d2203aff03829754773a707a8a5987f1b7682d92 + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.2.0": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -6577,6 +6584,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + "anymatch@npm:^2.0.0": version: 2.0.0 resolution: "anymatch@npm:2.0.0" @@ -6688,7 +6702,7 @@ __metadata: languageName: node linkType: hard -"arg@npm:5.0.2": +"arg@npm:5.0.2, arg@npm:^5.0.2": version: 5.0.2 resolution: "arg@npm:5.0.2" checksum: ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e @@ -7004,6 +7018,24 @@ __metadata: languageName: node linkType: hard +"autoprefixer@npm:^10.4.16": + version: 10.4.16 + resolution: "autoprefixer@npm:10.4.16" + dependencies: + browserslist: ^4.21.10 + caniuse-lite: ^1.0.30001538 + fraction.js: ^4.3.6 + normalize-range: ^0.1.2 + picocolors: ^1.0.0 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: e00256e754d481a026d928bca729b25954074dd142dbec022f0a7db0d3bbc0dc2e2dc7542e94fec22eff81e21fe140e6856448e2d9a002660cb1e2ad434daee0 + languageName: node + linkType: hard + "available-typed-arrays@npm:^1.0.5": version: 1.0.5 resolution: "available-typed-arrays@npm:1.0.5" @@ -7585,7 +7617,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": +"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": version: 4.22.1 resolution: "browserslist@npm:4.22.1" dependencies: @@ -7799,6 +7831,13 @@ __metadata: languageName: node linkType: hard +"camelcase-css@npm:^2.0.1": + version: 2.0.1 + resolution: "camelcase-css@npm:2.0.1" + checksum: 1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 + languageName: node + linkType: hard + "camelcase@npm:6.3.0, camelcase@npm:^6.2.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" @@ -7832,6 +7871,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001538": + version: 1.0.30001561 + resolution: "caniuse-lite@npm:1.0.30001561" + checksum: 6e84c84026fee53edbdbb5aded7a04a036aae4c2e367cf6bdc90c6783a591e2fdcfcdebcc4e774aca61092e542a61200c8c16b06659396492426033c4dbcc618 + languageName: node + linkType: hard + "capital-case@npm:^1.0.4": version: 1.0.4 resolution: "capital-case@npm:1.0.4" @@ -8381,7 +8427,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^4.0.1": +"commander@npm:^4.0.0, commander@npm:^4.0.1": version: 4.1.1 resolution: "commander@npm:4.1.1" checksum: 84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab @@ -8708,6 +8754,23 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:^8.2.0": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" + dependencies: + import-fresh: ^3.3.0 + js-yaml: ^4.1.0 + parse-json: ^5.2.0 + path-type: ^4.0.0 + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 0382a9ed13208f8bfc22ca2f62b364855207dffdb73dc26e150ade78c3093f1cf56172df2dd460c8caf2afa91c0ed4ec8a88c62f8f9cd1cf423d26506aa8797a + languageName: node + linkType: hard + "crc-32@npm:^1.2.0": version: 1.2.2 resolution: "crc-32@npm:1.2.2" @@ -9461,6 +9524,13 @@ __metadata: languageName: node linkType: hard +"didyoumean@npm:^1.2.2": + version: 1.2.2 + resolution: "didyoumean@npm:1.2.2" + checksum: 95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b + languageName: node + linkType: hard + "diff-sequences@npm:^29.6.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" @@ -9495,6 +9565,13 @@ __metadata: languageName: node linkType: hard +"dlv@npm:^1.1.3": + version: 1.1.3 + resolution: "dlv@npm:1.1.3" + checksum: 03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42 + languageName: node + linkType: hard + "dns-equal@npm:^1.0.0": version: 1.0.0 resolution: "dns-equal@npm:1.0.0" @@ -11253,6 +11330,13 @@ __metadata: languageName: node linkType: hard +"fraction.js@npm:^4.3.6": + version: 4.3.7 + resolution: "fraction.js@npm:4.3.7" + checksum: df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 + languageName: node + linkType: hard + "fragment-cache@npm:^0.2.1": version: 0.2.1 resolution: "fragment-cache@npm:0.2.1" @@ -11566,6 +11650,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:7.1.6": + version: 7.1.6 + resolution: "glob@npm:7.1.6" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 2575cce9306ac534388db751f0aa3e78afedb6af8f3b529ac6b2354f66765545145dba8530abf7bff49fb399a047d3f9b6901c38ee4c9503f592960d9af67763 + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.2.5": version: 10.3.10 resolution: "glob@npm:10.3.10" @@ -12363,7 +12461,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -13660,6 +13758,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.18.2, jiti@npm:^1.19.1": + version: 1.21.0 + resolution: "jiti@npm:1.21.0" + bin: + jiti: bin/jiti.js + checksum: 7f361219fe6c7a5e440d5f1dba4ab763a5538d2df8708cdc22561cf25ea3e44b837687931fca7cdd8cdd9f567300e90be989dd1321650045012d8f9ed6aab07f + languageName: node + linkType: hard + "jose@npm:^4.11.4": version: 4.15.2 resolution: "jose@npm:4.15.2" @@ -14078,7 +14185,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^2.1.0": +"lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" checksum: 64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 @@ -15060,6 +15167,17 @@ __metadata: languageName: node linkType: hard +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: ^1.0.0 + object-assign: ^4.0.1 + thenify-all: ^1.0.0 + checksum: 103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + "nan@npm:^2.12.1": version: 2.18.0 resolution: "nan@npm:2.18.0" @@ -15322,6 +15440,13 @@ __metadata: languageName: node linkType: hard +"normalize-range@npm:^0.1.2": + version: 0.1.2 + resolution: "normalize-range@npm:0.1.2" + checksum: bf39b73a63e0a42ad1a48c2bd1bda5a07ede64a7e2567307a407674e595bcff0fa0d57e8e5f1e7fa5e91000797c7615e13613227aaaa4d6d6e87f5bd5cc95de6 + languageName: node + linkType: hard + "normalize-url@npm:^4.1.0": version: 4.5.1 resolution: "normalize-url@npm:4.5.1" @@ -15415,7 +15540,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -15433,6 +15558,13 @@ __metadata: languageName: node linkType: hard +"object-hash@npm:^3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47 + languageName: node + linkType: hard + "object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": version: 1.12.3 resolution: "object-inspect@npm:1.12.3" @@ -16072,6 +16204,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc + languageName: node + linkType: hard + "pify@npm:^3.0.0": version: 3.0.0 resolution: "pify@npm:3.0.0" @@ -16145,7 +16284,7 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.4, pirates@npm:^4.0.5": +"pirates@npm:^4.0.1, pirates@npm:^4.0.4, pirates@npm:^4.0.5": version: 4.0.6 resolution: "pirates@npm:4.0.6" checksum: 00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 @@ -16287,6 +16426,62 @@ __metadata: languageName: node linkType: hard +"postcss-import@npm:^15.1.0": + version: 15.1.0 + resolution: "postcss-import@npm:15.1.0" + dependencies: + postcss-value-parser: ^4.0.0 + read-cache: ^1.0.0 + resolve: ^1.1.7 + peerDependencies: + postcss: ^8.0.0 + checksum: 518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b + languageName: node + linkType: hard + +"postcss-js@npm:^4.0.1": + version: 4.0.1 + resolution: "postcss-js@npm:4.0.1" + dependencies: + camelcase-css: ^2.0.1 + peerDependencies: + postcss: ^8.4.21 + checksum: af35d55cb873b0797d3b42529514f5318f447b134541844285c9ac31a17497297eb72296902967911bb737a75163441695737300ce2794e3bd8c70c13a3b106e + languageName: node + linkType: hard + +"postcss-load-config@npm:^4.0.1": + version: 4.0.1 + resolution: "postcss-load-config@npm:4.0.1" + dependencies: + lilconfig: ^2.0.5 + yaml: ^2.1.1 + peerDependencies: + postcss: ">=8.0.9" + ts-node: ">=9.0.0" + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + checksum: 5f568420c4d758d77d661f26914c08fe8dfb0666c7b779dc4f48d7fd880d131e8aa232a45cc1a8ba3f47f9c5fca572b661ca0103c2212979e9dc00918cff3d5f + languageName: node + linkType: hard + +"postcss-loader@npm:^7.3.3": + version: 7.3.3 + resolution: "postcss-loader@npm:7.3.3" + dependencies: + cosmiconfig: ^8.2.0 + jiti: ^1.18.2 + semver: ^7.3.8 + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + checksum: d039654273f858be1f75dfdf8b550869d88905b73a7684b3e48a2937a6087619e84fd1a3551cdef78685a965a2573e985b29a532c3878d834071ecd2da0eb304 + languageName: node + linkType: hard + "postcss-merge-longhand@npm:^6.0.0": version: 6.0.0 resolution: "postcss-merge-longhand@npm:6.0.0" @@ -16405,6 +16600,17 @@ __metadata: languageName: node linkType: hard +"postcss-nested@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-nested@npm:6.0.1" + dependencies: + postcss-selector-parser: ^6.0.11 + peerDependencies: + postcss: ^8.2.14 + checksum: 2a50aa36d5d103c2e471954830489f4c024deed94fa066169101db55171368d5f80b32446b584029e0471feee409293d0b6b1d8ede361f6675ba097e477b3cbd + languageName: node + linkType: hard + "postcss-normalize-charset@npm:^6.0.0": version: 6.0.0 resolution: "postcss-normalize-charset@npm:6.0.0" @@ -16571,14 +16777,14 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 languageName: node linkType: hard -"postcss@npm:^8.2.14, postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.27": +"postcss@npm:^8.2.14, postcss@npm:^8.4.21, postcss@npm:^8.4.23, postcss@npm:^8.4.24, postcss@npm:^8.4.27, postcss@npm:^8.4.31": version: 8.4.31 resolution: "postcss@npm:8.4.31" dependencies: @@ -16612,6 +16818,61 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-tailwindcss@npm:0.4.1": + version: 0.4.1 + resolution: "prettier-plugin-tailwindcss@npm:0.4.1" + peerDependencies: + "@ianvs/prettier-plugin-sort-imports": "*" + "@prettier/plugin-pug": "*" + "@shopify/prettier-plugin-liquid": "*" + "@shufo/prettier-plugin-blade": "*" + "@trivago/prettier-plugin-sort-imports": "*" + prettier: ^2.2 || ^3.0 + prettier-plugin-astro: "*" + prettier-plugin-css-order: "*" + prettier-plugin-import-sort: "*" + prettier-plugin-jsdoc: "*" + prettier-plugin-marko: "*" + prettier-plugin-organize-attributes: "*" + prettier-plugin-organize-imports: "*" + prettier-plugin-style-order: "*" + prettier-plugin-svelte: "*" + prettier-plugin-twig-melody: "*" + peerDependenciesMeta: + "@ianvs/prettier-plugin-sort-imports": + optional: true + "@prettier/plugin-pug": + optional: true + "@shopify/prettier-plugin-liquid": + optional: true + "@shufo/prettier-plugin-blade": + optional: true + "@trivago/prettier-plugin-sort-imports": + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true + checksum: da9dc4c8c80b5510d51e671de61bc230e2b076bd4c14ddd3e8bd4fa5395373800d10473718bbf22e1cabe09167e8d2eb1bedaa06587ee905d6e6106003b69d11 + languageName: node + linkType: hard + "prettier@npm:2.8.8, prettier@npm:^2.6.2": version: 2.8.8 resolution: "prettier@npm:2.8.8" @@ -17089,6 +17350,15 @@ __metadata: languageName: node linkType: hard +"read-cache@npm:^1.0.0": + version: 1.0.0 + resolution: "read-cache@npm:1.0.0" + dependencies: + pify: ^2.3.0 + checksum: 90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 + languageName: node + linkType: hard + "read-pkg-up@npm:7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" @@ -17494,7 +17764,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4": +"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -17533,7 +17803,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.11.1#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.4#~builtin": +"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.11.1#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.2#~builtin, resolve@patch:resolve@^1.22.4#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -17699,6 +17969,7 @@ __metadata: resolution: "root-workspace-0b6124@workspace:." dependencies: "@redwoodjs/core": 6.3.2 + prettier-plugin-tailwindcss: 0.4.1 languageName: unknown linkType: soft @@ -18884,6 +19155,24 @@ __metadata: languageName: node linkType: hard +"sucrase@npm:^3.32.0": + version: 3.34.0 + resolution: "sucrase@npm:3.34.0" + dependencies: + "@jridgewell/gen-mapping": ^0.3.2 + commander: ^4.0.0 + glob: 7.1.6 + lines-and-columns: ^1.1.6 + mz: ^2.7.0 + pirates: ^4.0.1 + ts-interface-checker: ^0.1.9 + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 83e524f2b9386c7029fc9e46b8d608485866d08bea5a0a71e9e3442dc12e1d05a5ab555808d1922f45dd012fc71043479d778aac07391d9740daabe45730a056 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -18977,6 +19266,39 @@ __metadata: languageName: node linkType: hard +"tailwindcss@npm:^3.3.5": + version: 3.3.5 + resolution: "tailwindcss@npm:3.3.5" + dependencies: + "@alloc/quick-lru": ^5.2.0 + arg: ^5.0.2 + chokidar: ^3.5.3 + didyoumean: ^1.2.2 + dlv: ^1.1.3 + fast-glob: ^3.3.0 + glob-parent: ^6.0.2 + is-glob: ^4.0.3 + jiti: ^1.19.1 + lilconfig: ^2.1.0 + micromatch: ^4.0.5 + normalize-path: ^3.0.0 + object-hash: ^3.0.0 + picocolors: ^1.0.0 + postcss: ^8.4.23 + postcss-import: ^15.1.0 + postcss-js: ^4.0.1 + postcss-load-config: ^4.0.1 + postcss-nested: ^6.0.1 + postcss-selector-parser: ^6.0.11 + resolve: ^1.22.2 + sucrase: ^3.32.0 + bin: + tailwind: lib/cli.js + tailwindcss: lib/cli.js + checksum: a57c0a9cdba9db19097e34e25b7e4690fab43f31ba200afc3bb9635a03036ca93e9884a17b616fb8a2486d57d2ecc9a06862ce4685b3ace57f7a67436e7594a0 + languageName: node + linkType: hard + "tapable@npm:^1.0.0, tapable@npm:^1.1.3": version: 1.1.3 resolution: "tapable@npm:1.1.3" @@ -19134,6 +19456,24 @@ __metadata: languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: ">= 3.1.0 < 4" + checksum: 9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: ^1.0.0 + checksum: f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + "thread-stream@npm:^2.0.0": version: 2.4.1 resolution: "thread-stream@npm:2.4.1" @@ -19347,6 +19687,13 @@ __metadata: languageName: node linkType: hard +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 + languageName: node + linkType: hard + "ts-invariant@npm:^0.10.3": version: 0.10.3 resolution: "ts-invariant@npm:0.10.3" @@ -20267,9 +20614,14 @@ __metadata: "@redwoodjs/web": 6.3.2 "@types/react": 18.2.14 "@types/react-dom": 18.2.6 + autoprefixer: ^10.4.16 + humanize-string: 2.1.0 + postcss: ^8.4.31 + postcss-loader: ^7.3.3 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0 + tailwindcss: ^3.3.5 languageName: unknown linkType: soft @@ -20883,6 +21235,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.1.1": + version: 2.3.4 + resolution: "yaml@npm:2.3.4" + checksum: cf03b68f8fef5e8516b0f0b54edaf2459f1648317fc6210391cf606d247e678b449382f4bd01f77392538429e306c7cba8ff46ff6b37cac4de9a76aff33bd9e1 + languageName: node + linkType: hard + "yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1"