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 (
+
+ )
+}
+
+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 (
+
+
+
+
+ Id |
+ Name |
+ Preis |
+ Kategorie |
+ |
+
+
+
+ {artikels.map((artikel) => (
+
+ {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 (
+
+
+
+
+ Id |
+ Name |
+ Preis |
+ Kategorie |
+ |
+
+
+
+ {listKategoryArtikels.map((kategorieartikel) => (<>
+
+ {kategorieartikel.name}
+
+
+ {kategorieartikel["artikel"].map((artikel) => (
+
+ {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 (
+
+ )
+}
+
+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 (
+
+ )
+}
+
+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 (
+
+
+
+
+ Id |
+ Name |
+ |
+
+
+
+ {kategories.map((kategorie) => (
+
+ {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 (
+
+ )
+}
+
+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 (
+
+ )
+}
+
+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 (
+
+
+
+
+ Id |
+ Uhrzeit |
+
+
+
+ {kaufs.map((kauf) => (
+
+ {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 (
+
+
+
+ Id |
+ Timestamp |
+ Preis ges |
+ |
+
+
+
+ {kaufs.map((kauf) => (
+
+ {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 (
+
+ )
+}
+
+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 (
+
+
+
+ {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 (
+
+
+
+ {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"