Re-init DB and scaffold all models

This commit is contained in:
Kurt Hutten
2020-11-02 07:02:11 +11:00
parent 55f9dfd6de
commit 86df1d501d
97 changed files with 3202 additions and 2122 deletions

View File

@@ -1,45 +0,0 @@
# Migration `20201009213512-create-posts`
This migration has been generated by Kurt Hutten at 10/10/2020, 8:35:12 AM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "Post" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
)
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20201009213512-create-posts
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,18 @@
+datasource DS {
+ // optionally set multiple providers
+ // example: provider = ["sqlite", "postgresql"]
+ provider = "sqlite"
+ url = "***"
+}
+
+generator client {
+ provider = "prisma-client-js"
+ binaryTargets = "native"
+}
+
+model Post {
+ id Int @id @default(autoincrement())
+ title String
+ body String
+ createdAt DateTime @default(now())
+}
```

View File

@@ -1,18 +0,0 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}

View File

@@ -1,120 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "DS"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "DS"
},
"argument": "provider",
"value": "\"sqlite\""
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "DS"
},
"argument": "url",
"value": "\"***\""
},
{
"tag": "CreateModel",
"model": "Post"
},
{
"tag": "CreateField",
"model": "Post",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Post",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Post",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Post",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateField",
"model": "Post",
"field": "title",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Post",
"field": "body",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Post",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Post",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Post",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
}
]
}

View File

@@ -1,52 +0,0 @@
# Migration `20201011043647-create-parts`
This migration has been generated by Kurt Hutten at 10/11/2020, 3:36:47 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "Part" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"mainImage" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
)
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201009213512-create-posts..20201011043647-create-parts
--- datamodel.dml
+++ datamodel.dml
@@ -1,9 +1,9 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -15,4 +15,14 @@
title String
body String
createdAt DateTime @default(now())
}
+
+model Part {
+ id Int @id @default(autoincrement())
+ title String
+ description String // markdown string
+ mainImage String // link to cloudinary
+ createdAt DateTime @default(now())
+ // userId
+ //likes, comments, reactions
+}
```

View File

@@ -1,28 +0,0 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}

View File

@@ -1,105 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateModel",
"model": "Part"
},
{
"tag": "CreateField",
"model": "Part",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateField",
"model": "Part",
"field": "title",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Part",
"field": "description",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Part",
"field": "mainImage",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Part",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
}
]
}

View File

@@ -1,65 +0,0 @@
# Migration `20201011052155-add-code-to-part`
This migration has been generated by Kurt Hutten at 10/11/2020, 4:21:55 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Part" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"code" TEXT NOT NULL DEFAULT '// Welcome to Cascade Studio! Here are some useful functions:
// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()
// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()
// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),
// FilletEdges(), ChamferEdges(),
// Slider(), Button(), Checkbox()
let holeRadius = Slider("Radius", 30 , 20 , 40);
let sphere = Sphere(50);
let cylinderZ = Cylinder(holeRadius, 200, true);/nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));
let cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));/nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));
Translate([-25, 0, 40], Text3D("Hi!"));/n// Don''t forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!',
"mainImage" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO "new_Part" ("id", "title", "description", "mainImage", "createdAt") SELECT "id", "title", "description", "mainImage", "createdAt" FROM "Part";
DROP TABLE "Part";
ALTER TABLE "new_Part" RENAME TO "Part";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201011043647-create-parts..20201011052155-add-code-to-part
--- datamodel.dml
+++ datamodel.dml
@@ -1,9 +1,9 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -20,8 +20,9 @@
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
+ code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);/nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));/nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));/n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
```

View File

@@ -1,29 +0,0 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);/nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));/nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));/n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}

View File

@@ -1,37 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateField",
"model": "Part",
"field": "code",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "code"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "code"
},
"directive": "default"
},
"argument": "",
"value": "\"// Welcome to Cascade Studio! Here are some useful functions:\\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\\n// FilletEdges(), ChamferEdges(),\\n// Slider(), Button(), Checkbox()\\nlet holeRadius = Slider(\\\"Radius\\\", 30 , 20 , 40);\\nlet sphere = Sphere(50);\\nlet cylinderZ = Cylinder(holeRadius, 200, true);/nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));/nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\\n\\nTranslate([-25, 0, 40], Text3D(\\\"Hi!\\\"));/n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!\""
}
]
}

View File

@@ -1,69 +0,0 @@
# Migration `20201011082558-add-code-not-needed-upon-create`
This migration has been generated by Kurt Hutten at 10/11/2020, 7:25:58 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Part" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"code" TEXT NOT NULL DEFAULT '// Welcome to Cascade Studio! Here are some useful functions:
// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()
// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()
// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),
// FilletEdges(), ChamferEdges(),
// Slider(), Button(), Checkbox()
let holeRadius = Slider("Radius", 30 , 20 , 40);
let sphere = Sphere(50);
let cylinderZ = Cylinder(holeRadius, 200, true);
let cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));
let cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));
Translate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));
Translate([-25, 0, 40], Text3D("Hi!"));
// Don''t forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!',
"mainImage" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO "new_Part" ("id", "title", "description", "mainImage", "createdAt", "code") SELECT "id", "title", "description", "mainImage", "createdAt", "code" FROM "Part";
DROP TABLE "Part";
ALTER TABLE "new_Part" RENAME TO "Part";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201011052155-add-code-to-part..20201011082558-add-code-not-needed-upon-create
--- datamodel.dml
+++ datamodel.dml
@@ -1,9 +1,9 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -20,9 +20,9 @@
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
- code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);/nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));/nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));/n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
+ code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
```

View File

@@ -1,29 +0,0 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}

View File

@@ -1,19 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "UpdateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "code"
},
"directive": "default"
},
"argument": "",
"newValue": "\"// Welcome to Cascade Studio! Here are some useful functions:\\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\\n// FilletEdges(), ChamferEdges(),\\n// Slider(), Button(), Checkbox()\\nlet holeRadius = Slider(\\\"Radius\\\", 30 , 20 , 40);\\nlet sphere = Sphere(50);\\nlet cylinderZ = Cylinder(holeRadius, 200, true);\\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\\n\\nTranslate([-25, 0, 40], Text3D(\\\"Hi!\\\"));\\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!\""
}
]
}

View File

@@ -1,50 +0,0 @@
# Migration `20201011095227-create-contact`
This migration has been generated by Kurt Hutten at 10/11/2020, 8:52:27 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "Contact" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"message" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
)
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201011082558-add-code-not-needed-upon-create..20201011095227-create-contact
--- datamodel.dml
+++ datamodel.dml
@@ -1,9 +1,9 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -26,4 +26,12 @@
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}
+
+model Contact {
+ id Int @id @default(autoincrement())
+ name String
+ email String
+ message String
+ createdAt DateTime @default(now())
+}
```

View File

@@ -1,37 +0,0 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}
model Contact {
id Int @id @default(autoincrement())
name String
email String
message String
createdAt DateTime @default(now())
}

View File

@@ -1,105 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateModel",
"model": "Contact"
},
{
"tag": "CreateField",
"model": "Contact",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Contact",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Contact",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Contact",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateField",
"model": "Contact",
"field": "name",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Contact",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Contact",
"field": "message",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Contact",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Contact",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Contact",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
}
]
}

View File

@@ -1,65 +0,0 @@
# Migration `20201018233330-add-simple-user-model`
This migration has been generated by Kurt Hutten at 10/19/2020, 10:33:30 AM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"userName" TEXT NOT NULL,
"email" TEXT NOT NULL,
"issuer" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"image" TEXT,
"bio" TEXT
)
CREATE UNIQUE INDEX "User.userName_unique" ON "User"("userName")
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email")
CREATE UNIQUE INDEX "User.issuer_unique" ON "User"("issuer")
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201011095227-create-contact..20201018233330-add-simple-user-model
--- datamodel.dml
+++ datamodel.dml
@@ -1,9 +1,7 @@
datasource DS {
- // optionally set multiple providers
- // example: provider = ["sqlite", "postgresql"]
- provider = "sqlite"
- url = "***"
+ provider = ["sqlite", "postgresql"]
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -34,4 +32,17 @@
email String
message String
createdAt DateTime @default(now())
}
+
+model User {
+ id Int @id @default(autoincrement())
+ userName String @unique
+ email String @unique
+ issuer String @unique
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ image String? // url maybe id or file storage service? cloudinary?
+ bio String? //mark down
+}
```

View File

@@ -1,48 +0,0 @@
datasource DS {
provider = ["sqlite", "postgresql"]
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}
model Contact {
id Int @id @default(autoincrement())
name String
email String
message String
createdAt DateTime @default(now())
}
model User {
id Int @id @default(autoincrement())
userName String @unique
email String @unique
issuer String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
image String? // url maybe id or file storage service? cloudinary?
bio String? //mark down
}

View File

@@ -1,179 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "UpdateArgument",
"location": {
"tag": "Source",
"source": "DS"
},
"argument": "provider",
"newValue": "[\"sqlite\", \"postgresql\"]"
},
{
"tag": "CreateModel",
"model": "User"
},
{
"tag": "CreateField",
"model": "User",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateField",
"model": "User",
"field": "userName",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "email"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "issuer",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "issuer"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "User",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "image",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "bio",
"type": "String",
"arity": "Optional"
}
]
}

View File

@@ -1,61 +0,0 @@
# Migration `20201019072122-add-simplify-user-model`
This migration has been generated by Kurt Hutten at 10/19/2020, 6:21:22 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
DROP INDEX "User.issuer_unique"
DROP INDEX "User.userName_unique"
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"image" TEXT,
"bio" TEXT
);
INSERT INTO "new_User" ("id", "email", "createdAt", "updatedAt", "image", "bio") SELECT "id", "email", "createdAt", "updatedAt", "image", "bio" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201018233330-add-simple-user-model..20201019072122-add-simplify-user-model
--- datamodel.dml
+++ datamodel.dml
@@ -1,7 +1,7 @@
datasource DS {
provider = ["sqlite", "postgresql"]
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -34,12 +34,12 @@
createdAt DateTime @default(now())
}
model User {
- id Int @id @default(autoincrement())
- userName String @unique
- email String @unique
- issuer String @unique
+ id Int @id @default(autoincrement())
+ email String @unique
+ // userName String @unique
+ // issuer String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
```

View File

@@ -1,48 +0,0 @@
datasource DS {
provider = ["sqlite", "postgresql"]
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
model Part {
id Int @id @default(autoincrement())
title String
description String // markdown string
code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
mainImage String // link to cloudinary
createdAt DateTime @default(now())
// userId
//likes, comments, reactions
}
model Contact {
id Int @id @default(autoincrement())
name String
email String
message String
createdAt DateTime @default(now())
}
model User {
id Int @id @default(autoincrement())
email String @unique
// userName String @unique
// issuer String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
image String? // url maybe id or file storage service? cloudinary?
bio String? //mark down
}

View File

@@ -1,15 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "DeleteField",
"model": "User",
"field": "userName"
},
{
"tag": "DeleteField",
"model": "User",
"field": "issuer"
}
]
}

View File

@@ -1,188 +0,0 @@
# Migration `20201029214206-rejig-schema`
This migration has been generated by Kurt Hutten at 10/30/2020, 8:42:06 AM.
You can check out the [state of the schema](./schema.prisma) after the migration.
# explanation
The previous schema was really just thrown together to progress with parts of the client.
No relations had been included so this change in reality can be consider the initial schema.
## Database Steps
```sql
CREATE TABLE "PartReaction" (
"emote" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"partId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE
)
CREATE TABLE "Comment" (
"id" TEXT NOT NULL,
"text" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("id") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("id") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("id")
)
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Part" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"code" TEXT,
"mainImage" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorUserName" TEXT NOT NULL DEFAULT 'irevdev',
FOREIGN KEY ("authorUserName") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("title","authorUserName")
);
INSERT INTO "new_Part" ("id", "title", "description", "mainImage", "createdAt", "code") SELECT "id", "title", "description", "mainImage", "createdAt", "code" FROM "Part";
DROP TABLE "Part";
ALTER TABLE "new_Part" RENAME TO "Part";
CREATE UNIQUE INDEX "Part.id_unique" ON "Part"("id");
CREATE UNIQUE INDEX "Part.title_authorUserName_unique" ON "Part"("title", "authorUserName");
CREATE TABLE "new_User" (
"userName" TEXT NOT NULL DEFAULT 'irevdev',
"email" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"image" TEXT,
"bio" TEXT,
PRIMARY KEY ("userName")
);
INSERT INTO "new_User" ("email", "createdAt", "updatedAt", "image", "bio") SELECT "email", "createdAt", "updatedAt", "image", "bio" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON
CREATE UNIQUE INDEX "PartReaction.emote_userId_partId_unique" ON "PartReaction"("emote", "userId", "partId")
PRAGMA foreign_keys=off;
DROP TABLE "Post";
PRAGMA foreign_keys=on
PRAGMA foreign_keys=off;
DROP TABLE "Contact";
PRAGMA foreign_keys=on
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201019072122-add-simplify-user-model..20201029214206-rejig-schema
--- datamodel.dml
+++ datamodel.dml
@@ -1,48 +1,76 @@
datasource DS {
provider = ["sqlite", "postgresql"]
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
-model Post {
- id Int @id @default(autoincrement())
- title String
- body String
+// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
+// enum Role {
+// USER
+// ADMIN
+// }
+
+// enum PartType {
+// CASCADESTUDIO
+// JSCAD
+// }
+
+model User {
+ userName String @id @default("irevdev")
+ email String @unique
+ // role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
+ // maybe let netlify handle roles for now.
+ // role String @default("user")
+
createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ image String? // url maybe id or file storage service? cloudinary?
+ bio String? //mark down
+ Part Part[]
+ Reaction PartReaction[]
+ Comment Comment[]
}
model Part {
- id Int @id @default(autoincrement())
- title String
- description String // markdown string
- code String @default("// Welcome to Cascade Studio! Here are some useful functions:\n// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()\n// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()\n// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),\n// FilletEdges(), ChamferEdges(),\n// Slider(), Button(), Checkbox()\nlet holeRadius = Slider(\"Radius\", 30 , 20 , 40);\nlet sphere = Sphere(50);\nlet cylinderZ = Cylinder(holeRadius, 200, true);\nlet cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));\nlet cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));\nTranslate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));\n\nTranslate([-25, 0, 40], Text3D(\"Hi!\"));\n// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!")
- mainImage String // link to cloudinary
- createdAt DateTime @default(now())
- // userId
- //likes, comments, reactions
+ id String @unique @default(uuid())
+ title String
+ description String? // markdown string
+ code String?
+ mainImage String? // link to cloudinary
+ createdAt DateTime @default(now())
+ updatedAt DateTime @default(now()) @updatedAt
+ author User @relation(fields: [authorUserName], references: [userName])
+ authorUserName String @default("irevdev")
+
+ Comment Comment[]
+ Reaction PartReaction[]
+ @@id([title, authorUserName])
+ @@unique([title, authorUserName])
}
-model Contact {
- id Int @id @default(autoincrement())
- name String
- email String
- message String
+model PartReaction {
+ emote String // an emoji
+ userId String
+ user User @relation(fields: [userId], references: [userName])
+ partId String
+ part Part @relation(fields: [partId], references: [id])
+
createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ @@unique([emote, userId, partId])
}
-model User {
- id Int @id @default(autoincrement())
- email String @unique
- // userName String @unique
- // issuer String @unique
+model Comment {
+ id String @id @default(uuid())
+ text String // the comment, should I allow mark down?
+ user User @relation(fields: [id], references: [userName])
+ part Part @relation(fields: [id], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
-
- image String? // url maybe id or file storage service? cloudinary?
- bio String? //mark down
}
```

View File

@@ -1,76 +0,0 @@
datasource DS {
provider = ["sqlite", "postgresql"]
url = "***"
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
// enum Role {
// USER
// ADMIN
// }
// enum PartType {
// CASCADESTUDIO
// JSCAD
// }
model User {
userName String @id @default(uuid())
email String @unique
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
// maybe let netlify handle roles for now.
// role String @default("user")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
image String? // url maybe id or file storage service? cloudinary?
bio String? //mark down
Part Part[]
Reaction PartReaction[]
Comment Comment[]
}
model Part {
id String @unique @default(uuid())
title String
description String? // markdown string
code String?
mainImage String? // link to cloudinary
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorUserName], references: [userName])
authorUserName String
Comment Comment[]
Reaction PartReaction[]
@@id([title, authorUserName])
@@unique([title, authorUserName])
}
model PartReaction {
emote String // an emoji
userId String
user User @relation(fields: [userId], references: [userName])
partId String
part Part @relation(fields: [partId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([emote, userId, partId])
}
model Comment {
id String @id @default(uuid())
text String // the comment, should I allow mark down?
user User @relation(fields: [id], references: [userName])
part Part @relation(fields: [id], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

View File

@@ -1,91 +0,0 @@
# Migration `20201030061957-rejig-schema`
This migration has been generated by Kurt Hutten at 10/30/2020, 5:19:57 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "User" (
"userName" TEXT NOT NULL,
"email" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"image" TEXT,
"bio" TEXT,
PRIMARY KEY ("userName")
)
CREATE TABLE "Part" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"code" TEXT,
"mainImage" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"authorUserName" TEXT NOT NULL,
FOREIGN KEY ("authorUserName") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("title","authorUserName")
)
CREATE TABLE "PartReaction" (
"emote" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"partId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE
)
CREATE TABLE "Comment" (
"id" TEXT NOT NULL,
"text" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("id") REFERENCES "User"("userName") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("id") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("id")
)
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email")
CREATE UNIQUE INDEX "Part.id_unique" ON "Part"("id")
CREATE UNIQUE INDEX "Part.title_authorUserName_unique" ON "Part"("title", "authorUserName")
CREATE UNIQUE INDEX "PartReaction.emote_userId_partId_unique" ON "PartReaction"("emote", "userId", "partId")
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201029214206-rejig-schema..20201030061957-rejig-schema
--- datamodel.dml
+++ datamodel.dml
@@ -1,7 +1,7 @@
datasource DS {
provider = ["sqlite", "postgresql"]
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -19,9 +19,9 @@
// JSCAD
// }
model User {
- userName String @id @default(uuid())
+ userName String @id
email String @unique
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
// maybe let netlify handle roles for now.
// role String @default("user")
```

View File

@@ -1,38 +0,0 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "DeleteDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "updatedAt"
},
"directive": "default"
}
},
{
"tag": "DeleteDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "authorUserName"
},
"directive": "default"
}
},
{
"tag": "DeleteDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "default"
}
}
]
}

View File

@@ -0,0 +1,158 @@
# Migration `20201101183848-db-init`
This migration has been generated by Kurt Hutten at 11/2/2020, 5:38:48 AM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"userName" TEXT NOT NULL,
"email" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"image" TEXT,
"bio" TEXT,
PRIMARY KEY ("id")
)
CREATE TABLE "Part" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"code" TEXT,
"mainImage" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" TEXT NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("id")
)
CREATE TABLE "PartReaction" (
"id" TEXT NOT NULL,
"emote" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"partId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("id")
)
CREATE TABLE "Comment" (
"id" TEXT NOT NULL,
"text" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"partId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ("id")
)
CREATE UNIQUE INDEX "User.userName_unique" ON "User"("userName")
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email")
CREATE UNIQUE INDEX "Part.title_userId_unique" ON "Part"("title", "userId")
CREATE UNIQUE INDEX "PartReaction.emote_userId_partId_unique" ON "PartReaction"("emote", "userId", "partId")
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20201101183848-db-init
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,79 @@
+datasource DS {
+ provider = ["sqlite", "postgresql"]
+ url = "***"
+}
+
+generator client {
+ provider = "prisma-client-js"
+ binaryTargets = "native"
+}
+
+// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
+// enum Role {
+// USER
+// ADMIN
+// }
+
+// enum PartType {
+// CASCADESTUDIO
+// JSCAD
+// }
+
+model User {
+ id String @id @default(uuid())
+ userName String @unique // reffered to as userId in @relations
+ email String @unique
+ // role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
+ // maybe let netlify handle roles for now.
+ // role String @default("user")
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ image String? // url maybe id or file storage service? cloudinary?
+ bio String? //mark down
+ Part Part[]
+ Reaction PartReaction[]
+ Comment Comment[]
+}
+
+model Part {
+ id String @id @default(uuid())
+ title String
+ description String? // markdown string
+ code String?
+ mainImage String? // link to cloudinary
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ user User @relation(fields: [userId], references: [id])
+ userId String
+
+ Comment Comment[]
+ Reaction PartReaction[]
+ @@unique([title, userId])
+}
+
+model PartReaction {
+ id String @id @default(uuid())
+ emote String // an emoji
+ user User @relation(fields: [userId], references: [id])
+ userId String
+ part Part @relation(fields: [partId], references: [id])
+ partId String
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ @@unique([emote, userId, partId])
+}
+
+model Comment {
+ id String @id @default(uuid())
+ text String // the comment, should I allow mark down?
+ user User @relation(fields: [userId], references: [id])
+ userId String
+ part Part @relation(fields: [partId], references: [id])
+ partId String
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
```

View File

@@ -20,7 +20,8 @@ generator client {
// }
model User {
userName String @id
id String @id @default(uuid())
userName String @unique // reffered to as userId in @relations
email String @unique
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
// maybe let netlify handle roles for now.
@@ -37,28 +38,28 @@ model User {
}
model Part {
id String @unique @default(uuid())
title String
description String? // markdown string
code String?
mainImage String? // link to cloudinary
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorUserName], references: [userName])
authorUserName String
id String @id @default(uuid())
title String
description String? // markdown string
code String?
mainImage String? // link to cloudinary
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
userId String
Comment Comment[]
Reaction PartReaction[]
@@id([title, authorUserName])
@@unique([title, authorUserName])
@@unique([title, userId])
}
model PartReaction {
id String @id @default(uuid())
emote String // an emoji
user User @relation(fields: [userId], references: [id])
userId String
user User @relation(fields: [userId], references: [userName])
partId String
part Part @relation(fields: [partId], references: [id])
partId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -66,10 +67,12 @@ model PartReaction {
}
model Comment {
id String @id @default(uuid())
text String // the comment, should I allow mark down?
user User @relation(fields: [id], references: [userName])
part Part @relation(fields: [id], references: [id])
id String @id @default(uuid())
text String // the comment, should I allow mark down?
user User @relation(fields: [userId], references: [id])
userId String
part Part @relation(fields: [partId], references: [id])
partId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

View File

@@ -1,6 +1,404 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "DS"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "DS"
},
"argument": "provider",
"value": "[\"sqlite\", \"postgresql\"]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "DS"
},
"argument": "url",
"value": "\"***\""
},
{
"tag": "CreateModel",
"model": "User"
},
{
"tag": "CreateField",
"model": "User",
"field": "id",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "uuid()"
},
{
"tag": "CreateField",
"model": "User",
"field": "userName",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "email"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "User",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "image",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "bio",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "Part",
"type": "Part",
"arity": "List"
},
{
"tag": "CreateField",
"model": "User",
"field": "Reaction",
"type": "PartReaction",
"arity": "List"
},
{
"tag": "CreateField",
"model": "User",
"field": "Comment",
"type": "Comment",
"arity": "List"
},
{
"tag": "CreateModel",
"model": "Part"
},
{
"tag": "CreateField",
"model": "Part",
"field": "id",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "uuid()"
},
{
"tag": "CreateField",
"model": "Part",
"field": "title",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Part",
"field": "description",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Part",
"field": "code",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Part",
"field": "mainImage",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Part",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "Part",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "Part",
"field": "user",
"type": "User",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "user"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "user"
},
"directive": "relation"
},
"argument": "fields",
"value": "[userId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "user"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Part",
"field": "userId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Part",
"field": "Comment",
"type": "Comment",
"arity": "List"
},
{
"tag": "CreateField",
"model": "Part",
"field": "Reaction",
"type": "PartReaction",
"arity": "List"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Model",
"model": "Part",
"arguments": [
{
"name": "",
"value": "[title, userId]"
}
]
},
"directive": "unique"
}
},
{
"tag": "CreateModel",
"model": "PartReaction"
@@ -8,14 +406,50 @@
{
"tag": "CreateField",
"model": "PartReaction",
"field": "emote",
"field": "id",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "uuid()"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "userId",
"field": "emote",
"type": "String",
"arity": "Required"
},
@@ -63,12 +497,12 @@
"directive": "relation"
},
"argument": "references",
"value": "[userName]"
"value": "[id]"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "partId",
"field": "userId",
"type": "String",
"arity": "Required"
},
@@ -118,6 +552,13 @@
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "partId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "PartReaction",
@@ -268,7 +709,7 @@
"directive": "relation"
},
"argument": "fields",
"value": "[id]"
"value": "[userId]"
},
{
"tag": "CreateArgument",
@@ -282,7 +723,14 @@
"directive": "relation"
},
"argument": "references",
"value": "[userName]"
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "userId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
@@ -314,7 +762,7 @@
"directive": "relation"
},
"argument": "fields",
"value": "[id]"
"value": "[partId]"
},
{
"tag": "CreateArgument",
@@ -330,6 +778,13 @@
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "partId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Comment",
@@ -379,328 +834,6 @@
},
"directive": "updatedAt"
}
},
{
"tag": "DeleteModel",
"model": "Post"
},
{
"tag": "DeleteModel",
"model": "Contact"
},
{
"tag": "CreateField",
"model": "Part",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "updatedAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "updatedAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "Part",
"field": "author",
"type": "User",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "author"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "author"
},
"directive": "relation"
},
"argument": "fields",
"value": "[authorUserName]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "author"
},
"directive": "relation"
},
"argument": "references",
"value": "[userName]"
},
{
"tag": "CreateField",
"model": "Part",
"field": "authorUserName",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "authorUserName"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "authorUserName"
},
"directive": "default"
},
"argument": "",
"value": "\"irevdev\""
},
{
"tag": "CreateField",
"model": "Part",
"field": "Comment",
"type": "Comment",
"arity": "List"
},
{
"tag": "CreateField",
"model": "Part",
"field": "Reaction",
"type": "PartReaction",
"arity": "List"
},
{
"tag": "UpdateField",
"model": "Part",
"field": "id",
"type": "String"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "unique"
}
},
{
"tag": "UpdateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "default"
},
"argument": "",
"newValue": "uuid()"
},
{
"tag": "DeleteDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "UpdateField",
"model": "Part",
"field": "description",
"arity": "Optional"
},
{
"tag": "UpdateField",
"model": "Part",
"field": "mainImage",
"arity": "Optional"
},
{
"tag": "UpdateField",
"model": "Part",
"field": "code",
"arity": "Optional"
},
{
"tag": "DeleteDirective",
"location": {
"path": {
"tag": "Field",
"model": "Part",
"field": "code"
},
"directive": "default"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Model",
"model": "Part"
},
"directive": "id"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Model",
"model": "Part"
},
"directive": "id"
},
"argument": "",
"value": "[title, authorUserName]"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Model",
"model": "Part",
"arguments": [
{
"name": "",
"value": "[title, authorUserName]"
}
]
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "userName",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "userName"
},
"directive": "default"
},
"argument": "",
"value": "\"irevdev\""
},
{
"tag": "CreateField",
"model": "User",
"field": "Part",
"type": "Part",
"arity": "List"
},
{
"tag": "CreateField",
"model": "User",
"field": "Reaction",
"type": "PartReaction",
"arity": "List"
},
{
"tag": "CreateField",
"model": "User",
"field": "Comment",
"type": "Comment",
"arity": "List"
},
{
"tag": "DeleteField",
"model": "User",
"field": "id"
}
]
}

View File

@@ -1,11 +1,3 @@
# Prisma Migrate lockfile v1
20201009213512-create-posts
20201011043647-create-parts
20201011052155-add-code-to-part
20201011082558-add-code-not-needed-upon-create
20201011095227-create-contact
20201018233330-add-simple-user-model
20201019072122-add-simplify-user-model
20201029214206-rejig-schema
20201030061957-rejig-schema
20201101183848-db-init

View File

@@ -20,7 +20,8 @@ generator client {
// }
model User {
userName String @id
id String @id @default(uuid())
userName String @unique // reffered to as userId in @relations
email String @unique
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
// maybe let netlify handle roles for now.
@@ -37,28 +38,28 @@ model User {
}
model Part {
id String @unique @default(uuid())
title String
description String? // markdown string
code String?
mainImage String? // link to cloudinary
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorUserName], references: [userName])
authorUserName String
id String @id @default(uuid())
title String
description String? // markdown string
code String?
mainImage String? // link to cloudinary
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
userId String
Comment Comment[]
Reaction PartReaction[]
@@id([title, authorUserName])
@@unique([title, authorUserName])
@@unique([title, userId])
}
model PartReaction {
id String @id @default(uuid())
emote String // an emoji
user User @relation(fields: [userId], references: [id])
userId String
user User @relation(fields: [userId], references: [userName])
partId String
part Part @relation(fields: [partId], references: [id])
partId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -66,10 +67,12 @@ model PartReaction {
}
model Comment {
id String @id @default(uuid())
text String // the comment, should I allow mark down?
user User @relation(fields: [id], references: [userName])
part Part @relation(fields: [id], references: [id])
id String @id @default(uuid())
text String // the comment, should I allow mark down?
user User @relation(fields: [userId], references: [id])
userId String
part Part @relation(fields: [partId], references: [id])
partId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

View File

@@ -1,4 +1,4 @@
import { createUserInsecure } from 'src/services/users/users.js'
// import { createUserInsecure } from 'src/services/users/users.js'
export const handler = async (req, _context) => {
const body = JSON.parse(req.body)
@@ -66,7 +66,7 @@ export const handler = async (req, _context) => {
bio: 'default bio'
// full_name: user.user_metadata.full_name
}
await createUserInsecure({input})
// await createUserInsecure({input})
return {
statusCode: 200,

View File

@@ -0,0 +1,35 @@
export const schema = gql`
type Comment {
id: String!
text: String!
user: User!
userId: String!
part: Part!
partId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
comments: [Comment!]!
comment(id: String!): Comment
}
input CreateCommentInput {
text: String!
userId: String!
partId: String!
}
input UpdateCommentInput {
text: String
userId: String
partId: String
}
type Mutation {
createComment(input: CreateCommentInput!): Comment!
updateComment(id: String!, input: UpdateCommentInput!): Comment!
deleteComment(id: String!): Comment!
}
`

View File

@@ -0,0 +1,38 @@
export const schema = gql`
type PartReaction {
id: String!
emote: String!
user: User!
userId: String!
part: Part!
partId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
partReactions: [PartReaction!]!
partReaction(id: String!): PartReaction
}
input CreatePartReactionInput {
emote: String!
userId: String!
partId: String!
}
input UpdatePartReactionInput {
emote: String
userId: String
partId: String
}
type Mutation {
createPartReaction(input: CreatePartReactionInput!): PartReaction!
updatePartReaction(
id: String!
input: UpdatePartReactionInput!
): PartReaction!
deletePartReaction(id: String!): PartReaction!
}
`

View File

@@ -0,0 +1,42 @@
export const schema = gql`
type Part {
id: String!
title: String!
description: String
code: String
mainImage: String
createdAt: DateTime!
updatedAt: DateTime!
user: User!
userId: String!
Comment: [Comment]!
Reaction: [PartReaction]!
}
type Query {
parts: [Part!]!
part(id: String!): Part
}
input CreatePartInput {
title: String!
description: String
code: String
mainImage: String
userId: String!
}
input UpdatePartInput {
title: String
description: String
code: String
mainImage: String
userId: String
}
type Mutation {
createPart(input: CreatePartInput!): Part!
updatePart(id: String!, input: UpdatePartInput!): Part!
deletePart(id: String!): Part!
}
`

View File

@@ -0,0 +1,39 @@
export const schema = gql`
type User {
id: String!
userName: String!
email: String!
createdAt: DateTime!
updatedAt: DateTime!
image: String
bio: String
Part: [Part]!
Reaction: [PartReaction]!
Comment: [Comment]!
}
type Query {
users: [User!]!
user(id: String!): User
}
input CreateUserInput {
userName: String!
email: String!
image: String
bio: String
}
input UpdateUserInput {
userName: String
email: String
image: String
bio: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: String!, input: UpdateUserInput!): User!
deleteUser(id: String!): User!
}
`

View File

@@ -0,0 +1,38 @@
import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers'
export const comments = () => {
return db.comment.findMany()
}
export const comment = ({ id }) => {
return db.comment.findOne({
where: { id },
})
}
export const createComment = ({ input }) => {
return db.comment.create({
data: foreignKeyReplacement(input),
})
}
export const updateComment = ({ id, input }) => {
return db.comment.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deleteComment = ({ id }) => {
return db.comment.delete({
where: { id },
})
}
export const Comment = {
user: (_obj, { root }) =>
db.comment.findOne({ where: { id: root.id } }).user(),
part: (_obj, { root }) =>
db.comment.findOne({ where: { id: root.id } }).part(),
}

View File

@@ -0,0 +1,9 @@
/*
import { comments } from './comments'
*/
describe('comments', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,13 @@
export const foreignKeyReplacement = (input) => {
let output = input
const foreignKeys = Object.keys(input).filter((k) => k.match(/Id$/))
foreignKeys.forEach((key) => {
const modelName = key.replace(/Id$/, '')
const value = input[key]
delete output[key]
output = Object.assign(output, {
[modelName]: { connect: { id: value } },
})
})
return output
}

View File

@@ -0,0 +1,38 @@
import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers'
export const partReactions = () => {
return db.partReaction.findMany()
}
export const partReaction = ({ id }) => {
return db.partReaction.findOne({
where: { id },
})
}
export const createPartReaction = ({ input }) => {
return db.partReaction.create({
data: foreignKeyReplacement(input),
})
}
export const updatePartReaction = ({ id, input }) => {
return db.partReaction.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deletePartReaction = ({ id }) => {
return db.partReaction.delete({
where: { id },
})
}
export const PartReaction = {
user: (_obj, { root }) =>
db.partReaction.findOne({ where: { id: root.id } }).user(),
part: (_obj, { root }) =>
db.partReaction.findOne({ where: { id: root.id } }).part(),
}

View File

@@ -0,0 +1,9 @@
/*
import { partReactions } from './partReactions'
*/
describe('partReactions', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,40 @@
import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers'
import { user } from 'src/services/users/users'
export const parts = () => {
return db.part.findMany()
}
export const part = ({ id }) => {
return db.part.findOne({
where: { id },
})
}
export const createPart = async ({ input }) => {
return db.part.create({
data: foreignKeyReplacement(input),
})
}
export const updatePart = ({ id, input }) => {
return db.part.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deletePart = ({ id }) => {
return db.part.delete({
where: { id },
})
}
export const Part = {
user: (_obj, { root }) => db.part.findOne({ where: { id: root.id } }).user(),
Comment: (_obj, { root }) =>
db.part.findOne({ where: { id: root.id } }).Comment(),
Reaction: (_obj, { root }) =>
db.part.findOne({ where: { id: root.id } }).Reaction(),
}

View File

@@ -0,0 +1,9 @@
/*
import { parts } from './parts'
*/
describe('parts', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,38 @@
import { db } from 'src/lib/db'
export const users = () => {
return db.user.findMany()
}
export const user = ({ id }) => {
return db.user.findOne({
where: { id },
})
}
export const createUser = ({ input }) => {
return db.user.create({
data: input,
})
}
export const updateUser = ({ id, input }) => {
return db.user.update({
data: input,
where: { id },
})
}
export const deleteUser = ({ id }) => {
return db.user.delete({
where: { id },
})
}
export const User = {
Part: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(),
Reaction: (_obj, { root }) =>
db.user.findOne({ where: { id: root.id } }).Reaction(),
Comment: (_obj, { root }) =>
db.user.findOne({ where: { id: root.id } }).Comment(),
}

View File

@@ -0,0 +1,9 @@
/*
import { users } from './users'
*/
describe('users', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -12,8 +12,24 @@ import { Router, Route, Private } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/" page={PartsPage} name="home" />
<Route path="/blah/*" page={PartsPage} name="home" />
<Route path="/part-reactions/new" page={NewPartReactionPage} name="newPartReaction" />
<Route path="/part-reactions/{id}/edit" page={EditPartReactionPage} name="editPartReaction" />
<Route path="/part-reactions/{id}" page={PartReactionPage} name="partReaction" />
<Route path="/part-reactions" page={PartReactionsPage} name="partReactions" />
<Route path="/parts/new" page={NewPartPage} name="newPart" />
<Route path="/parts/{id}/edit" page={EditPartPage} name="editPart" />
<Route path="/parts/{id}" page={PartPage} name="part" />
<Route path="/parts" page={PartsPage} name="parts" />
<Route path="/comments/new" page={NewCommentPage} name="newComment" />
<Route path="/comments/{id}/edit" page={EditCommentPage} name="editComment" />
<Route path="/comments/{id}" page={CommentPage} name="comment" />
<Route path="/comments" page={CommentsPage} name="comments" />
<Route path="/users/new" page={NewUserPage} name="newUser" />
<Route path="/users/{id}/edit" page={EditUserPage} name="editUser" />
<Route path="/users/{id}" page={UserPage} name="user" />
<Route path="/users" page={UsersPage} name="users" />
<Route path="/" page={HomePage} name="home" />
{/* <Route path="/blah/*" page={PartsPage} name="home" /> */}
<Route notfound page={NotFoundPage} />
</Router>
)

View File

@@ -0,0 +1,103 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
const DELETE_COMMENT_MUTATION = gql`
mutation DeleteCommentMutation($id: String!) {
deleteComment(id: $id) {
id
}
}
`
const jsonDisplay = (obj) => {
return (
<pre>
<code>{JSON.stringify(obj, null, 2)}</code>
</pre>
)
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const Comment = ({ comment }) => {
const { addMessage } = useFlash()
const [deleteComment] = useMutation(DELETE_COMMENT_MUTATION, {
onCompleted: () => {
navigate(routes.comments())
addMessage('Comment deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete comment ' + id + '?')) {
deleteComment({ variables: { id } })
}
}
return (
<>
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Comment {comment.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{comment.id}</td>
</tr>
<tr>
<th>Text</th>
<td>{comment.text}</td>
</tr>
<tr>
<th>User id</th>
<td>{comment.userId}</td>
</tr>
<tr>
<th>Part id</th>
<td>{comment.partId}</td>
</tr>
<tr>
<th>Created at</th>
<td>{timeTag(comment.createdAt)}</td>
</tr>
<tr>
<th>Updated at</th>
<td>{timeTag(comment.updatedAt)}</td>
</tr>
</tbody>
</table>
</div>
<nav className="rw-button-group">
<Link
to={routes.editComment({ id: comment.id })}
className="rw-button rw-button-blue"
>
Edit
</Link>
<a
href="#"
className="rw-button rw-button-red"
onClick={() => onDeleteClick(comment.id)}
>
Delete
</a>
</nav>
</>
)
}
export default Comment

View File

@@ -0,0 +1,22 @@
import Comment from 'src/components/Comment'
export const QUERY = gql`
query FIND_COMMENT_BY_ID($id: String!) {
comment: comment(id: $id) {
id
text
userId
partId
createdAt
updatedAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Comment not found</div>
export const Success = ({ comment }) => {
return <Comment comment={comment} />
}

View File

@@ -0,0 +1,83 @@
import {
Form,
FormError,
FieldError,
Label,
TextField,
Submit,
} from '@redwoodjs/forms'
const CommentForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.comment?.id)
}
return (
<div className="rw-form-wrapper">
<Form onSubmit={onSubmit} error={props.error}>
<FormError
error={props.error}
wrapperClassName="rw-form-error-wrapper"
titleClassName="rw-form-error-title"
listClassName="rw-form-error-list"
/>
<Label
name="text"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Text
</Label>
<TextField
name="text"
defaultValue={props.comment?.text}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="text" className="rw-field-error" />
<Label
name="userId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
User id
</Label>
<TextField
name="userId"
defaultValue={props.comment?.userId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="userId" className="rw-field-error" />
<Label
name="partId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Part id
</Label>
<TextField
name="partId"
defaultValue={props.comment?.partId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="partId" className="rw-field-error" />
<div className="rw-button-group">
<Submit disabled={props.loading} className="rw-button rw-button-blue">
Save
</Submit>
</div>
</Form>
</div>
)
}
export default CommentForm

View File

@@ -0,0 +1,109 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_COMMENT_MUTATION = gql`
mutation DeleteCommentMutation($id: String!) {
deleteComment(id: $id) {
id
}
}
`
const MAX_STRING_LENGTH = 150
const truncate = (text) => {
let output = text
if (text && text.length > MAX_STRING_LENGTH) {
output = output.substring(0, MAX_STRING_LENGTH) + '...'
}
return output
}
const jsonTruncate = (obj) => {
return truncate(JSON.stringify(obj, null, 2))
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const CommentsList = ({ comments }) => {
const { addMessage } = useFlash()
const [deleteComment] = useMutation(DELETE_COMMENT_MUTATION, {
onCompleted: () => {
addMessage('Comment deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete comment ' + id + '?')) {
deleteComment({ variables: { id }, refetchQueries: ['COMMENTS'] })
}
}
return (
<div className="rw-segment rw-table-wrapper-responsive">
<table className="rw-table">
<thead>
<tr>
<th>Id</th>
<th>Text</th>
<th>User id</th>
<th>Part id</th>
<th>Created at</th>
<th>Updated at</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{comments.map((comment) => (
<tr key={comment.id}>
<td>{truncate(comment.id)}</td>
<td>{truncate(comment.text)}</td>
<td>{truncate(comment.userId)}</td>
<td>{truncate(comment.partId)}</td>
<td>{timeTag(comment.createdAt)}</td>
<td>{timeTag(comment.updatedAt)}</td>
<td>
<nav className="rw-table-actions">
<Link
to={routes.comment({ id: comment.id })}
title={'Show comment ' + comment.id + ' detail'}
className="rw-button rw-button-small"
>
Show
</Link>
<Link
to={routes.editComment({ id: comment.id })}
title={'Edit comment ' + comment.id}
className="rw-button rw-button-small rw-button-blue"
>
Edit
</Link>
<a
href="#"
title={'Delete comment ' + comment.id}
className="rw-button rw-button-small rw-button-red"
onClick={() => onDeleteClick(comment.id)}
>
Delete
</a>
</nav>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default CommentsList

View File

@@ -0,0 +1,33 @@
import { Link, routes } from '@redwoodjs/router'
import Comments from 'src/components/Comments'
export const QUERY = gql`
query COMMENTS {
comments {
id
text
userId
partId
createdAt
updatedAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => {
return (
<div className="rw-text-center">
{'No comments yet. '}
<Link to={routes.newComment()} className="rw-link">
{'Create one?'}
</Link>
</div>
)
}
export const Success = ({ comments }) => {
return <Comments comments={comments} />
}

View File

@@ -0,0 +1,60 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import CommentForm from 'src/components/CommentForm'
export const QUERY = gql`
query FIND_COMMENT_BY_ID($id: String!) {
comment: comment(id: $id) {
id
text
userId
partId
createdAt
updatedAt
}
}
`
const UPDATE_COMMENT_MUTATION = gql`
mutation UpdateCommentMutation($id: String!, $input: UpdateCommentInput!) {
updateComment(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ comment }) => {
const { addMessage } = useFlash()
const [updateComment, { loading, error }] = useMutation(
UPDATE_COMMENT_MUTATION,
{
onCompleted: () => {
navigate(routes.comments())
addMessage('Comment updated.', { classes: 'rw-flash-success' })
},
}
)
const onSave = (input, id) => {
updateComment({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Edit Comment {comment.id}
</h2>
</header>
<div className="rw-segment-main">
<CommentForm
comment={comment}
onSave={onSave}
error={error}
loading={loading}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,52 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PartForm from 'src/components/PartForm'
export const QUERY = gql`
query FIND_PART_BY_ID($id: String!) {
part: part(id: $id) {
id
title
description
code
mainImage
createdAt
updatedAt
userId
}
}
`
const UPDATE_PART_MUTATION = gql`
mutation UpdatePartMutation($id: String!, $input: UpdatePartInput!) {
updatePart(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ part }) => {
const { addMessage } = useFlash()
const [updatePart, { loading, error }] = useMutation(UPDATE_PART_MUTATION, {
onCompleted: () => {
navigate(routes.parts())
addMessage('Part updated.', { classes: 'rw-flash-success' })
},
})
const onSave = (input, id) => {
updatePart({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">Edit Part {part.id}</h2>
</header>
<div className="rw-segment-main">
<PartForm part={part} onSave={onSave} error={error} loading={loading} />
</div>
</div>
)
}

View File

@@ -0,0 +1,63 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PartReactionForm from 'src/components/PartReactionForm'
export const QUERY = gql`
query FIND_PART_REACTION_BY_ID($id: String!) {
partReaction: partReaction(id: $id) {
id
emote
userId
partId
createdAt
updatedAt
}
}
`
const UPDATE_PART_REACTION_MUTATION = gql`
mutation UpdatePartReactionMutation(
$id: String!
$input: UpdatePartReactionInput!
) {
updatePartReaction(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ partReaction }) => {
const { addMessage } = useFlash()
const [updatePartReaction, { loading, error }] = useMutation(
UPDATE_PART_REACTION_MUTATION,
{
onCompleted: () => {
navigate(routes.partReactions())
addMessage('PartReaction updated.', { classes: 'rw-flash-success' })
},
}
)
const onSave = (input, id) => {
updatePartReaction({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Edit PartReaction {partReaction.id}
</h2>
</header>
<div className="rw-segment-main">
<PartReactionForm
partReaction={partReaction}
onSave={onSave}
error={error}
loading={loading}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,51 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import UserForm from 'src/components/UserForm'
export const QUERY = gql`
query FIND_USER_BY_ID($id: String!) {
user: user(id: $id) {
id
userName
email
createdAt
updatedAt
image
bio
}
}
`
const UPDATE_USER_MUTATION = gql`
mutation UpdateUserMutation($id: String!, $input: UpdateUserInput!) {
updateUser(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ user }) => {
const { addMessage } = useFlash()
const [updateUser, { loading, error }] = useMutation(UPDATE_USER_MUTATION, {
onCompleted: () => {
navigate(routes.users())
addMessage('User updated.', { classes: 'rw-flash-success' })
},
})
const onSave = (input, id) => {
updateUser({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">Edit User {user.id}</h2>
</header>
<div className="rw-segment-main">
<UserForm user={user} onSave={onSave} error={error} loading={loading} />
</div>
</div>
)
}

View File

@@ -1,6 +1,6 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import Part from 'src/components/Part'
// import Part from 'src/components/Part'
export const QUERY = gql`
query FIND_PART_BY_ID($id: Int!) {
@@ -42,5 +42,6 @@ export const Success = ({ part }) => {
console.log(id, input, 'wowow')
updatePart({ variables: { id, input } })
}
return <Part part={{...part, code: part.code}} saveCode={saveCode} loading={loading} error={error} />
return <div>TODO part</div>
// return <Part part={{...part, code: part.code}} saveCode={saveCode} loading={loading} error={error} />
}

View File

@@ -0,0 +1,41 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import CommentForm from 'src/components/CommentForm'
const CREATE_COMMENT_MUTATION = gql`
mutation CreateCommentMutation($input: CreateCommentInput!) {
createComment(input: $input) {
id
}
}
`
const NewComment = () => {
const { addMessage } = useFlash()
const [createComment, { loading, error }] = useMutation(
CREATE_COMMENT_MUTATION,
{
onCompleted: () => {
navigate(routes.comments())
addMessage('Comment created.', { classes: 'rw-flash-success' })
},
}
)
const onSave = (input) => {
createComment({ variables: { input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">New Comment</h2>
</header>
<div className="rw-segment-main">
<CommentForm onSave={onSave} loading={loading} error={error} />
</div>
</div>
)
}
export default NewComment

View File

@@ -0,0 +1,38 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PartForm from 'src/components/PartForm'
const CREATE_PART_MUTATION = gql`
mutation CreatePartMutation($input: CreatePartInput!) {
createPart(input: $input) {
id
}
}
`
const NewPart = () => {
const { addMessage } = useFlash()
const [createPart, { loading, error }] = useMutation(CREATE_PART_MUTATION, {
onCompleted: () => {
navigate(routes.parts())
addMessage('Part created.', { classes: 'rw-flash-success' })
},
})
const onSave = (input) => {
createPart({ variables: { input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">New Part</h2>
</header>
<div className="rw-segment-main">
<PartForm onSave={onSave} loading={loading} error={error} />
</div>
</div>
)
}
export default NewPart

View File

@@ -0,0 +1,41 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PartReactionForm from 'src/components/PartReactionForm'
const CREATE_PART_REACTION_MUTATION = gql`
mutation CreatePartReactionMutation($input: CreatePartReactionInput!) {
createPartReaction(input: $input) {
id
}
}
`
const NewPartReaction = () => {
const { addMessage } = useFlash()
const [createPartReaction, { loading, error }] = useMutation(
CREATE_PART_REACTION_MUTATION,
{
onCompleted: () => {
navigate(routes.partReactions())
addMessage('PartReaction created.', { classes: 'rw-flash-success' })
},
}
)
const onSave = (input) => {
createPartReaction({ variables: { input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">New PartReaction</h2>
</header>
<div className="rw-segment-main">
<PartReactionForm onSave={onSave} loading={loading} error={error} />
</div>
</div>
)
}
export default NewPartReaction

View File

@@ -0,0 +1,38 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import UserForm from 'src/components/UserForm'
const CREATE_USER_MUTATION = gql`
mutation CreateUserMutation($input: CreateUserInput!) {
createUser(input: $input) {
id
}
}
`
const NewUser = () => {
const { addMessage } = useFlash()
const [createUser, { loading, error }] = useMutation(CREATE_USER_MUTATION, {
onCompleted: () => {
navigate(routes.users())
addMessage('User created.', { classes: 'rw-flash-success' })
},
})
const onSave = (input) => {
createUser({ variables: { input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">New User</h2>
</header>
<div className="rw-segment-main">
<UserForm onSave={onSave} loading={loading} error={error} />
</div>
</div>
)
}
export default NewUser

View File

@@ -0,0 +1,111 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
const DELETE_PART_MUTATION = gql`
mutation DeletePartMutation($id: String!) {
deletePart(id: $id) {
id
}
}
`
const jsonDisplay = (obj) => {
return (
<pre>
<code>{JSON.stringify(obj, null, 2)}</code>
</pre>
)
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const Part = ({ part }) => {
const { addMessage } = useFlash()
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
onCompleted: () => {
navigate(routes.parts())
addMessage('Part deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete part ' + id + '?')) {
deletePart({ variables: { id } })
}
}
return (
<>
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Part {part.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{part.id}</td>
</tr>
<tr>
<th>Title</th>
<td>{part.title}</td>
</tr>
<tr>
<th>Description</th>
<td>{part.description}</td>
</tr>
<tr>
<th>Code</th>
<td>{part.code}</td>
</tr>
<tr>
<th>Main image</th>
<td>{part.mainImage}</td>
</tr>
<tr>
<th>Created at</th>
<td>{timeTag(part.createdAt)}</td>
</tr>
<tr>
<th>Updated at</th>
<td>{timeTag(part.updatedAt)}</td>
</tr>
<tr>
<th>User id</th>
<td>{part.userId}</td>
</tr>
</tbody>
</table>
</div>
<nav className="rw-button-group">
<Link
to={routes.editPart({ id: part.id })}
className="rw-button rw-button-blue"
>
Edit
</Link>
<a
href="#"
className="rw-button rw-button-red"
onClick={() => onDeleteClick(part.id)}
>
Delete
</a>
</nav>
</>
)
}
export default Part

View File

@@ -0,0 +1,24 @@
import Part from 'src/components/Part'
export const QUERY = gql`
query FIND_PART_BY_ID($id: String!) {
part: part(id: $id) {
id
title
description
code
mainImage
createdAt
updatedAt
userId
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Part not found</div>
export const Success = ({ part }) => {
return <Part part={part} />
}

View File

@@ -1,120 +0,0 @@
import React, { useCallback, useState } from "react";
import { useDropzone } from "react-dropzone";
import Button from "@material-ui/core/Button";
import axios from 'axios'
import ReactCrop from 'react-image-crop'
import { Dialog } from '@material-ui/core'
import { Image as CloudinaryImage } from 'cloudinary-react'
import 'react-image-crop/dist/ReactCrop.css'
import Svg from 'src/components/Svg/Svg.js'
const CLOUDINARY_UPLOAD_PRESET = "CadHub_project_images";
const CLOUDINARY_UPLOAD_URL = "https://api.cloudinary.com/v1_1/irevdev/upload";
export default function ImageUploader({ onImageUpload, imageUrl }) {
const [isModalOpen, setIsModalOpen] = useState(false)
const [file, setFile] = useState()
const [cloudinaryId, setCloudinaryId] = useState(imageUrl)
const [imageObj, setImageObj] = useState()
const [crop, setCrop] = useState({
aspect: 16 / 9,
unit: '%',
width: 100,
});
async function handleImageUpload() {
const croppedFile = await getCroppedImg(imageObj, crop, 'avatar')
console.log(croppedFile)
const imageData = new FormData();
imageData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET);
imageData.append('file', croppedFile);
let upload = axios.post(CLOUDINARY_UPLOAD_URL, imageData)
try {
const { data } = await upload
if (data && data.public_id !== "") {
onImageUpload({cloudinaryPublicId: data.public_id})
setCloudinaryId(data.public_id)
setIsModalOpen(false)
}
} catch (e) {
console.error('ERROR', e)
}
}
// Drag and Drop
const onDrop = useCallback(acceptedFiles => {
setIsModalOpen(true)
const fileReader = new FileReader()
fileReader.onload = () => {
setFile(fileReader.result)
}
fileReader.readAsDataURL(acceptedFiles[0])
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
return (
<div className="m-8">
<div className="w-full relative" {...getRootProps()}>
{cloudinaryId && <button className="absolute z-10 w-full inset-0 bg-indigo-900 opacity-50 flex justify-center items-center">
<Svg name="pencil" strokeWidth={2} className="text-gray-300 h-48 w-48" />
</button>}
<input {...getInputProps()} />
{cloudinaryId && <div className="relative">
<CloudinaryImage
className="object-cover w-full rounded shadow"
cloudName="irevdev"
publicId={cloudinaryId}
width="600"
crop="scale"
/>
</div>}
{!cloudinaryId && <button className="absolute inset-0"></button>}
{!cloudinaryId && <div className="mt-3 text-indigo-500 border-dashed border border-indigo-500 py-8 text-center rounded-lg w-full">
Drop files here ...
or <span className="group flex w-full items-center justify-center py-4">
<span className="bg-indigo-500 shadow rounded text-gray-200 cursor-pointer p-2 hover:shadow-lg transform hover:-translate-y-1 transition-all duration-150">upload</span>
</span>
</div>}
</div>
<Dialog
open={isModalOpen}
onClose={() => setIsModalOpen(false)}
>
<div className="p-4">
<ReactCrop src={file} crop={crop} onImageLoaded={(image) => setImageObj(image)} onChange={newCrop => setCrop(newCrop)} />
<Button onClick={handleImageUpload} variant="outlined">Upload</Button>
</div>
</Dialog>
</div>
);
}
function getCroppedImg(image, crop, fileName) {
const canvas = document.createElement('canvas');
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
canvas.width = crop.width;
canvas.height = crop.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(
image,
crop.x * scaleX,
crop.y * scaleY,
crop.width * scaleX,
crop.height * scaleY,
0,
0,
crop.width,
crop.height
);
// As Base64 string
// const base64Image = canvas.toDataURL('image/jpeg');
// As a blob
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
blob.name = fileName;
resolve(blob);
}, 'image/jpeg', 1);
});
}

View File

@@ -0,0 +1,115 @@
import {
Form,
FormError,
FieldError,
Label,
TextField,
Submit,
} from '@redwoodjs/forms'
const PartForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.part?.id)
}
return (
<div className="rw-form-wrapper">
<Form onSubmit={onSubmit} error={props.error}>
<FormError
error={props.error}
wrapperClassName="rw-form-error-wrapper"
titleClassName="rw-form-error-title"
listClassName="rw-form-error-list"
/>
<Label
name="title"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Title
</Label>
<TextField
name="title"
defaultValue={props.part?.title}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="title" className="rw-field-error" />
<Label
name="description"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Description
</Label>
<TextField
name="description"
defaultValue={props.part?.description}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="description" className="rw-field-error" />
<Label
name="code"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Code
</Label>
<TextField
name="code"
defaultValue={props.part?.code}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="code" className="rw-field-error" />
<Label
name="mainImage"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Main image
</Label>
<TextField
name="mainImage"
defaultValue={props.part?.mainImage}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="mainImage" className="rw-field-error" />
<Label
name="userId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
User id
</Label>
<TextField
name="userId"
defaultValue={props.part?.userId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="userId" className="rw-field-error" />
<div className="rw-button-group">
<Submit disabled={props.loading} className="rw-button rw-button-blue">
Save
</Submit>
</div>
</Form>
</div>
)
}
export default PartForm

View File

@@ -0,0 +1,103 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
const DELETE_PART_REACTION_MUTATION = gql`
mutation DeletePartReactionMutation($id: String!) {
deletePartReaction(id: $id) {
id
}
}
`
const jsonDisplay = (obj) => {
return (
<pre>
<code>{JSON.stringify(obj, null, 2)}</code>
</pre>
)
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const PartReaction = ({ partReaction }) => {
const { addMessage } = useFlash()
const [deletePartReaction] = useMutation(DELETE_PART_REACTION_MUTATION, {
onCompleted: () => {
navigate(routes.partReactions())
addMessage('PartReaction deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete partReaction ' + id + '?')) {
deletePartReaction({ variables: { id } })
}
}
return (
<>
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
PartReaction {partReaction.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{partReaction.id}</td>
</tr>
<tr>
<th>Emote</th>
<td>{partReaction.emote}</td>
</tr>
<tr>
<th>User id</th>
<td>{partReaction.userId}</td>
</tr>
<tr>
<th>Part id</th>
<td>{partReaction.partId}</td>
</tr>
<tr>
<th>Created at</th>
<td>{timeTag(partReaction.createdAt)}</td>
</tr>
<tr>
<th>Updated at</th>
<td>{timeTag(partReaction.updatedAt)}</td>
</tr>
</tbody>
</table>
</div>
<nav className="rw-button-group">
<Link
to={routes.editPartReaction({ id: partReaction.id })}
className="rw-button rw-button-blue"
>
Edit
</Link>
<a
href="#"
className="rw-button rw-button-red"
onClick={() => onDeleteClick(partReaction.id)}
>
Delete
</a>
</nav>
</>
)
}
export default PartReaction

View File

@@ -0,0 +1,22 @@
import PartReaction from 'src/components/PartReaction'
export const QUERY = gql`
query FIND_PART_REACTION_BY_ID($id: String!) {
partReaction: partReaction(id: $id) {
id
emote
userId
partId
createdAt
updatedAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>PartReaction not found</div>
export const Success = ({ partReaction }) => {
return <PartReaction partReaction={partReaction} />
}

View File

@@ -0,0 +1,83 @@
import {
Form,
FormError,
FieldError,
Label,
TextField,
Submit,
} from '@redwoodjs/forms'
const PartReactionForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.partReaction?.id)
}
return (
<div className="rw-form-wrapper">
<Form onSubmit={onSubmit} error={props.error}>
<FormError
error={props.error}
wrapperClassName="rw-form-error-wrapper"
titleClassName="rw-form-error-title"
listClassName="rw-form-error-list"
/>
<Label
name="emote"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Emote
</Label>
<TextField
name="emote"
defaultValue={props.partReaction?.emote}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="emote" className="rw-field-error" />
<Label
name="userId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
User id
</Label>
<TextField
name="userId"
defaultValue={props.partReaction?.userId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="userId" className="rw-field-error" />
<Label
name="partId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Part id
</Label>
<TextField
name="partId"
defaultValue={props.partReaction?.partId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="partId" className="rw-field-error" />
<div className="rw-button-group">
<Submit disabled={props.loading} className="rw-button rw-button-blue">
Save
</Submit>
</div>
</Form>
</div>
)
}
export default PartReactionForm

View File

@@ -0,0 +1,112 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_PART_REACTION_MUTATION = gql`
mutation DeletePartReactionMutation($id: String!) {
deletePartReaction(id: $id) {
id
}
}
`
const MAX_STRING_LENGTH = 150
const truncate = (text) => {
let output = text
if (text && text.length > MAX_STRING_LENGTH) {
output = output.substring(0, MAX_STRING_LENGTH) + '...'
}
return output
}
const jsonTruncate = (obj) => {
return truncate(JSON.stringify(obj, null, 2))
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const PartReactionsList = ({ partReactions }) => {
const { addMessage } = useFlash()
const [deletePartReaction] = useMutation(DELETE_PART_REACTION_MUTATION, {
onCompleted: () => {
addMessage('PartReaction deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete partReaction ' + id + '?')) {
deletePartReaction({
variables: { id },
refetchQueries: ['PART_REACTIONS'],
})
}
}
return (
<div className="rw-segment rw-table-wrapper-responsive">
<table className="rw-table">
<thead>
<tr>
<th>Id</th>
<th>Emote</th>
<th>User id</th>
<th>Part id</th>
<th>Created at</th>
<th>Updated at</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{partReactions.map((partReaction) => (
<tr key={partReaction.id}>
<td>{truncate(partReaction.id)}</td>
<td>{truncate(partReaction.emote)}</td>
<td>{truncate(partReaction.userId)}</td>
<td>{truncate(partReaction.partId)}</td>
<td>{timeTag(partReaction.createdAt)}</td>
<td>{timeTag(partReaction.updatedAt)}</td>
<td>
<nav className="rw-table-actions">
<Link
to={routes.partReaction({ id: partReaction.id })}
title={'Show partReaction ' + partReaction.id + ' detail'}
className="rw-button rw-button-small"
>
Show
</Link>
<Link
to={routes.editPartReaction({ id: partReaction.id })}
title={'Edit partReaction ' + partReaction.id}
className="rw-button rw-button-small rw-button-blue"
>
Edit
</Link>
<a
href="#"
title={'Delete partReaction ' + partReaction.id}
className="rw-button rw-button-small rw-button-red"
onClick={() => onDeleteClick(partReaction.id)}
>
Delete
</a>
</nav>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default PartReactionsList

View File

@@ -0,0 +1,33 @@
import { Link, routes } from '@redwoodjs/router'
import PartReactions from 'src/components/PartReactions'
export const QUERY = gql`
query PART_REACTIONS {
partReactions {
id
emote
userId
partId
createdAt
updatedAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => {
return (
<div className="rw-text-center">
{'No partReactions yet. '}
<Link to={routes.newPartReaction()} className="rw-link">
{'Create one?'}
</Link>
</div>
)
}
export const Success = ({ partReactions }) => {
return <PartReactions partReactions={partReactions} />
}

View File

@@ -0,0 +1,113 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_PART_MUTATION = gql`
mutation DeletePartMutation($id: String!) {
deletePart(id: $id) {
id
}
}
`
const MAX_STRING_LENGTH = 150
const truncate = (text) => {
let output = text
if (text && text.length > MAX_STRING_LENGTH) {
output = output.substring(0, MAX_STRING_LENGTH) + '...'
}
return output
}
const jsonTruncate = (obj) => {
return truncate(JSON.stringify(obj, null, 2))
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const PartsList = ({ parts }) => {
const { addMessage } = useFlash()
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
onCompleted: () => {
addMessage('Part deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete part ' + id + '?')) {
deletePart({ variables: { id }, refetchQueries: ['PARTS'] })
}
}
return (
<div className="rw-segment rw-table-wrapper-responsive">
<table className="rw-table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Description</th>
<th>Code</th>
<th>Main image</th>
<th>Created at</th>
<th>Updated at</th>
<th>User id</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{parts.map((part) => (
<tr key={part.id}>
<td>{truncate(part.id)}</td>
<td>{truncate(part.title)}</td>
<td>{truncate(part.description)}</td>
<td>{truncate(part.code)}</td>
<td>{truncate(part.mainImage)}</td>
<td>{timeTag(part.createdAt)}</td>
<td>{timeTag(part.updatedAt)}</td>
<td>{truncate(part.userId)}</td>
<td>
<nav className="rw-table-actions">
<Link
to={routes.part({ id: part.id })}
title={'Show part ' + part.id + ' detail'}
className="rw-button rw-button-small"
>
Show
</Link>
<Link
to={routes.editPart({ id: part.id })}
title={'Edit part ' + part.id}
className="rw-button rw-button-small rw-button-blue"
>
Edit
</Link>
<a
href="#"
title={'Delete part ' + part.id}
className="rw-button rw-button-small rw-button-red"
onClick={() => onDeleteClick(part.id)}
>
Delete
</a>
</nav>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default PartsList

View File

@@ -0,0 +1,35 @@
import { Link, routes } from '@redwoodjs/router'
import Parts from 'src/components/Parts'
export const QUERY = gql`
query PARTS {
parts {
id
title
description
code
mainImage
createdAt
updatedAt
userId
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => {
return (
<div className="rw-text-center">
{'No parts yet. '}
<Link to={routes.newPart()} className="rw-link">
{'Create one?'}
</Link>
</div>
)
}
export const Success = ({ parts }) => {
return <Parts parts={parts} />
}

View File

@@ -0,0 +1,107 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
const DELETE_USER_MUTATION = gql`
mutation DeleteUserMutation($id: String!) {
deleteUser(id: $id) {
id
}
}
`
const jsonDisplay = (obj) => {
return (
<pre>
<code>{JSON.stringify(obj, null, 2)}</code>
</pre>
)
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const User = ({ user }) => {
const { addMessage } = useFlash()
const [deleteUser] = useMutation(DELETE_USER_MUTATION, {
onCompleted: () => {
navigate(routes.users())
addMessage('User deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete user ' + id + '?')) {
deleteUser({ variables: { id } })
}
}
return (
<>
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
User {user.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{user.id}</td>
</tr>
<tr>
<th>User name</th>
<td>{user.userName}</td>
</tr>
<tr>
<th>Email</th>
<td>{user.email}</td>
</tr>
<tr>
<th>Created at</th>
<td>{timeTag(user.createdAt)}</td>
</tr>
<tr>
<th>Updated at</th>
<td>{timeTag(user.updatedAt)}</td>
</tr>
<tr>
<th>Image</th>
<td>{user.image}</td>
</tr>
<tr>
<th>Bio</th>
<td>{user.bio}</td>
</tr>
</tbody>
</table>
</div>
<nav className="rw-button-group">
<Link
to={routes.editUser({ id: user.id })}
className="rw-button rw-button-blue"
>
Edit
</Link>
<a
href="#"
className="rw-button rw-button-red"
onClick={() => onDeleteClick(user.id)}
>
Delete
</a>
</nav>
</>
)
}
export default User

View File

@@ -0,0 +1,23 @@
import User from 'src/components/User'
export const QUERY = gql`
query FIND_USER_BY_ID($id: String!) {
user: user(id: $id) {
id
userName
email
createdAt
updatedAt
image
bio
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>User not found</div>
export const Success = ({ user }) => {
return <User user={user} />
}

View File

@@ -0,0 +1,99 @@
import {
Form,
FormError,
FieldError,
Label,
TextField,
Submit,
} from '@redwoodjs/forms'
const UserForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.user?.id)
}
return (
<div className="rw-form-wrapper">
<Form onSubmit={onSubmit} error={props.error}>
<FormError
error={props.error}
wrapperClassName="rw-form-error-wrapper"
titleClassName="rw-form-error-title"
listClassName="rw-form-error-list"
/>
<Label
name="userName"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
User name
</Label>
<TextField
name="userName"
defaultValue={props.user?.userName}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="userName" className="rw-field-error" />
<Label
name="email"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Email
</Label>
<TextField
name="email"
defaultValue={props.user?.email}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="email" className="rw-field-error" />
<Label
name="image"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Image
</Label>
<TextField
name="image"
defaultValue={props.user?.image}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="image" className="rw-field-error" />
<Label
name="bio"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Bio
</Label>
<TextField
name="bio"
defaultValue={props.user?.bio}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="bio" className="rw-field-error" />
<div className="rw-button-group">
<Submit disabled={props.loading} className="rw-button rw-button-blue">
Save
</Submit>
</div>
</Form>
</div>
)
}
export default UserForm

View File

@@ -0,0 +1,111 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_USER_MUTATION = gql`
mutation DeleteUserMutation($id: String!) {
deleteUser(id: $id) {
id
}
}
`
const MAX_STRING_LENGTH = 150
const truncate = (text) => {
let output = text
if (text && text.length > MAX_STRING_LENGTH) {
output = output.substring(0, MAX_STRING_LENGTH) + '...'
}
return output
}
const jsonTruncate = (obj) => {
return truncate(JSON.stringify(obj, null, 2))
}
const timeTag = (datetime) => {
return (
<time dateTime={datetime} title={datetime}>
{new Date(datetime).toUTCString()}
</time>
)
}
const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const UsersList = ({ users }) => {
const { addMessage } = useFlash()
const [deleteUser] = useMutation(DELETE_USER_MUTATION, {
onCompleted: () => {
addMessage('User deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete user ' + id + '?')) {
deleteUser({ variables: { id }, refetchQueries: ['USERS'] })
}
}
return (
<div className="rw-segment rw-table-wrapper-responsive">
<table className="rw-table">
<thead>
<tr>
<th>Id</th>
<th>User name</th>
<th>Email</th>
<th>Created at</th>
<th>Updated at</th>
<th>Image</th>
<th>Bio</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td>{truncate(user.id)}</td>
<td>{truncate(user.userName)}</td>
<td>{truncate(user.email)}</td>
<td>{timeTag(user.createdAt)}</td>
<td>{timeTag(user.updatedAt)}</td>
<td>{truncate(user.image)}</td>
<td>{truncate(user.bio)}</td>
<td>
<nav className="rw-table-actions">
<Link
to={routes.user({ id: user.id })}
title={'Show user ' + user.id + ' detail'}
className="rw-button rw-button-small"
>
Show
</Link>
<Link
to={routes.editUser({ id: user.id })}
title={'Edit user ' + user.id}
className="rw-button rw-button-small rw-button-blue"
>
Edit
</Link>
<a
href="#"
title={'Delete user ' + user.id}
className="rw-button rw-button-small rw-button-red"
onClick={() => onDeleteClick(user.id)}
>
Delete
</a>
</nav>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default UsersList

View File

@@ -0,0 +1,34 @@
import { Link, routes } from '@redwoodjs/router'
import Users from 'src/components/Users'
export const QUERY = gql`
query USERS {
users {
id
userName
email
createdAt
updatedAt
image
bio
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => {
return (
<div className="rw-text-center">
{'No users yet. '}
<Link to={routes.newUser()} className="rw-link">
{'Create one?'}
</Link>
</div>
)
}
export const Success = ({ users }) => {
return <Users users={users} />
}

View File

@@ -0,0 +1,23 @@
import { Link, routes } from '@redwoodjs/router'
import { Flash } from '@redwoodjs/web'
const CommentsLayout = (props) => {
return (
<div className="rw-scaffold">
<Flash timeout={1000} />
<header className="rw-header">
<h1 className="rw-heading rw-heading-primary">
<Link to={routes.comments()} className="rw-link">
Comments
</Link>
</h1>
<Link to={routes.newComment()} className="rw-button rw-button-green">
<div className="rw-button-icon">+</div> New Comment
</Link>
</header>
<main className="rw-main">{props.children}</main>
</div>
)
}
export default CommentsLayout

View File

@@ -18,7 +18,7 @@ const MainLayout = ({ children }) => {
<Link to={routes.home()}>
<Tooltip title="We need a logo!" >
<img src={logo} style={{marginLeft : '50px'}}/>
<img src={logo} style={{marginLeft : '50px'}}/>
</Tooltip>
</Link>
@@ -35,9 +35,9 @@ const MainLayout = ({ children }) => {
</ul>
<ul className="flex items-center">
<li className="mr-8 h-10 w-10 rounded-full border-2 border-indigo-300 flex items-center justify-center">
<Link to={routes.newPart()}>
{/* <Link to={routes.newPart()}> */}
<Svg name="plus" className="text-indigo-300" />
</Link>
{/* </Link> */}
</li>
{
isAuthenticated ?

View File

@@ -0,0 +1,26 @@
import { Link, routes } from '@redwoodjs/router'
import { Flash } from '@redwoodjs/web'
const PartReactionsLayout = (props) => {
return (
<div className="rw-scaffold">
<Flash timeout={1000} />
<header className="rw-header">
<h1 className="rw-heading rw-heading-primary">
<Link to={routes.partReactions()} className="rw-link">
PartReactions
</Link>
</h1>
<Link
to={routes.newPartReaction()}
className="rw-button rw-button-green"
>
<div className="rw-button-icon">+</div> New PartReaction
</Link>
</header>
<main className="rw-main">{props.children}</main>
</div>
)
}
export default PartReactionsLayout

View File

@@ -0,0 +1,23 @@
import { Link, routes } from '@redwoodjs/router'
import { Flash } from '@redwoodjs/web'
const PartsLayout = (props) => {
return (
<div className="rw-scaffold">
<Flash timeout={1000} />
<header className="rw-header">
<h1 className="rw-heading rw-heading-primary">
<Link to={routes.parts()} className="rw-link">
Parts
</Link>
</h1>
<Link to={routes.newPart()} className="rw-button rw-button-green">
<div className="rw-button-icon">+</div> New Part
</Link>
</header>
<main className="rw-main">{props.children}</main>
</div>
)
}
export default PartsLayout

View File

@@ -0,0 +1,23 @@
import { Link, routes } from '@redwoodjs/router'
import { Flash } from '@redwoodjs/web'
const UsersLayout = (props) => {
return (
<div className="rw-scaffold">
<Flash timeout={1000} />
<header className="rw-header">
<h1 className="rw-heading rw-heading-primary">
<Link to={routes.users()} className="rw-link">
Users
</Link>
</h1>
<Link to={routes.newUser()} className="rw-button rw-button-green">
<div className="rw-button-icon">+</div> New User
</Link>
</header>
<main className="rw-main">{props.children}</main>
</div>
)
}
export default UsersLayout

View File

@@ -0,0 +1,12 @@
import CommentsLayout from 'src/layouts/CommentsLayout'
import CommentCell from 'src/components/CommentCell'
const CommentPage = ({ id }) => {
return (
<CommentsLayout>
<CommentCell id={id} />
</CommentsLayout>
)
}
export default CommentPage

View File

@@ -0,0 +1,12 @@
import CommentsLayout from 'src/layouts/CommentsLayout'
import CommentsCell from 'src/components/CommentsCell'
const CommentsPage = () => {
return (
<CommentsLayout>
<CommentsCell />
</CommentsLayout>
)
}
export default CommentsPage

View File

@@ -0,0 +1,12 @@
import CommentsLayout from 'src/layouts/CommentsLayout'
import EditCommentCell from 'src/components/EditCommentCell'
const EditCommentPage = ({ id }) => {
return (
<CommentsLayout>
<EditCommentCell id={id} />
</CommentsLayout>
)
}
export default EditCommentPage

View File

@@ -0,0 +1,12 @@
import PartsLayout from 'src/layouts/PartsLayout'
import EditPartCell from 'src/components/EditPartCell'
const EditPartPage = ({ id }) => {
return (
<PartsLayout>
<EditPartCell id={id} />
</PartsLayout>
)
}
export default EditPartPage

View File

@@ -0,0 +1,12 @@
import PartReactionsLayout from 'src/layouts/PartReactionsLayout'
import EditPartReactionCell from 'src/components/EditPartReactionCell'
const EditPartReactionPage = ({ id }) => {
return (
<PartReactionsLayout>
<EditPartReactionCell id={id} />
</PartReactionsLayout>
)
}
export default EditPartReactionPage

View File

@@ -0,0 +1,12 @@
import UsersLayout from 'src/layouts/UsersLayout'
import EditUserCell from 'src/components/EditUserCell'
const EditUserPage = ({ id }) => {
return (
<UsersLayout>
<EditUserCell id={id} />
</UsersLayout>
)
}
export default EditUserPage

View File

@@ -1,5 +1,5 @@
import MainLayout from 'src/layouts/MainLayout'
import BlogPostsCell from 'src/components/BlogPostsCell'
// import BlogPostsCell from 'src/components/BlogPostsCell'
import { initialize } from 'src/cascade/js/MainPage/CascadeMain'
import { useEffect, useState } from 'react'
@@ -27,7 +27,7 @@ Translate([-100, 0, 100], Text3D("cadhub.xyz"));
// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!
`;
const HomePage = () => {
const HomePage1 = () => {
const [code, setCode] = useState(starterCode)
useEffect(() => {
const sickCallback = (code) => setCode(code)
@@ -73,4 +73,12 @@ const HomePage = () => {
)
}
const HomePage = () => {
return (
<MainLayout>
hi
</MainLayout>
)
}
export default HomePage

View File

@@ -0,0 +1,12 @@
import CommentsLayout from 'src/layouts/CommentsLayout'
import NewComment from 'src/components/NewComment'
const NewCommentPage = () => {
return (
<CommentsLayout>
<NewComment />
</CommentsLayout>
)
}
export default NewCommentPage

View File

@@ -0,0 +1,12 @@
import PartsLayout from 'src/layouts/PartsLayout'
import NewPart from 'src/components/NewPart'
const NewPartPage = () => {
return (
<PartsLayout>
<NewPart />
</PartsLayout>
)
}
export default NewPartPage

View File

@@ -0,0 +1,12 @@
import PartReactionsLayout from 'src/layouts/PartReactionsLayout'
import NewPartReaction from 'src/components/NewPartReaction'
const NewPartReactionPage = () => {
return (
<PartReactionsLayout>
<NewPartReaction />
</PartReactionsLayout>
)
}
export default NewPartReactionPage

View File

@@ -0,0 +1,12 @@
import UsersLayout from 'src/layouts/UsersLayout'
import NewUser from 'src/components/NewUser'
const NewUserPage = () => {
return (
<UsersLayout>
<NewUser />
</UsersLayout>
)
}
export default NewUserPage

View File

@@ -0,0 +1,12 @@
import PartsLayout from 'src/layouts/PartsLayout'
import PartCell from 'src/components/PartCell'
const PartPage = ({ id }) => {
return (
<PartsLayout>
<PartCell id={id} />
</PartsLayout>
)
}
export default PartPage

View File

@@ -0,0 +1,12 @@
import PartReactionsLayout from 'src/layouts/PartReactionsLayout'
import PartReactionCell from 'src/components/PartReactionCell'
const PartReactionPage = ({ id }) => {
return (
<PartReactionsLayout>
<PartReactionCell id={id} />
</PartReactionsLayout>
)
}
export default PartReactionPage

View File

@@ -0,0 +1,12 @@
import PartReactionsLayout from 'src/layouts/PartReactionsLayout'
import PartReactionsCell from 'src/components/PartReactionsCell'
const PartReactionsPage = () => {
return (
<PartReactionsLayout>
<PartReactionsCell />
</PartReactionsLayout>
)
}
export default PartReactionsPage

View File

@@ -0,0 +1,12 @@
import PartsLayout from 'src/layouts/PartsLayout'
import PartsCell from 'src/components/PartsCell'
const PartsPage = () => {
return (
<PartsLayout>
<PartsCell />
</PartsLayout>
)
}
export default PartsPage

View File

@@ -0,0 +1,12 @@
import UsersLayout from 'src/layouts/UsersLayout'
import UserCell from 'src/components/UserCell'
const UserPage = ({ id }) => {
return (
<UsersLayout>
<UserCell id={id} />
</UsersLayout>
)
}
export default UserPage

View File

@@ -0,0 +1,12 @@
import UsersLayout from 'src/layouts/UsersLayout'
import UsersCell from 'src/components/UsersCell'
const UsersPage = () => {
return (
<UsersLayout>
<UsersCell />
</UsersLayout>
)
}
export default UsersPage