Rejig database schema #58

Merged
Irev-Dev merged 5 commits from Rejig-database-schema into dev 2020-11-03 21:29:03 +01:00
122 changed files with 2455 additions and 2432 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

@@ -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

@@ -0,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

@@ -0,0 +1,839 @@
{
"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"
},
{
"tag": "CreateField",
"model": "PartReaction",
"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": "emote",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "user",
"type": "User",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "user"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "user"
},
"directive": "relation"
},
"argument": "fields",
"value": "[userId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "user"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "userId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "part",
"type": "Part",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "part"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "part"
},
"directive": "relation"
},
"argument": "fields",
"value": "[partId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "part"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "partId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "PartReaction",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "PartReaction",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Model",
"model": "PartReaction",
"arguments": [
{
"name": "",
"value": "[emote, userId, partId]"
}
]
},
"directive": "unique"
}
},
{
"tag": "CreateModel",
"model": "Comment"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "id",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "uuid()"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "text",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "user",
"type": "User",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "user"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "user"
},
"directive": "relation"
},
"argument": "fields",
"value": "[userId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "user"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "userId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "part",
"type": "Part",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "part"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "part"
},
"directive": "relation"
},
"argument": "fields",
"value": "[partId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "part"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "partId",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Comment",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "Comment",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Comment",
"field": "updatedAt"
},
"directive": "updatedAt"
}
}
]
}

View File

@@ -1,9 +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
20201101183848-db-init

View File

@@ -8,41 +8,72 @@ generator client {
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
// enum Role {
// USER
// ADMIN
// }
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())
}
// enum PartType {
// CASCADESTUDIO
// JSCAD
// }
model User {
id Int @id @default(autoincrement())
email String @unique
// userName String @unique
// issuer String @unique
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
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

@@ -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

@@ -1,29 +0,0 @@
export const schema = gql`
type Contact {
id: Int!
name: String!
email: String!
message: String!
createdAt: DateTime!
}
type Query {
contacts: [Contact!]!
}
input CreateContactInput {
name: String!
email: String!
message: String!
}
input UpdateContactInput {
name: String
email: String
message: String
}
type Mutation {
createContact(input: CreateContactInput!): Contact
}
`

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

@@ -1,23 +1,29 @@
export const schema = gql`
type Part {
id: Int!
id: String!
title: String!
description: String!
code: String!
mainImage: 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: Int!): Part
part(id: String!): Part
}
input CreatePartInput {
title: String!
description: String!
description: String
code: String
mainImage: String
userId: String!
}
input UpdatePartInput {
@@ -25,11 +31,12 @@ export const schema = gql`
description: String
code: String
mainImage: String
userId: String
}
type Mutation {
createPart(input: CreatePartInput!): Part!
updatePart(id: Int!, input: UpdatePartInput!): Part!
deletePart(id: Int!): Part!
updatePart(id: String!, input: UpdatePartInput!): Part!
deletePart(id: String!): Part!
}
`

View File

@@ -1,29 +0,0 @@
export const schema = gql`
type Post {
id: Int!
title: String!
body: String!
createdAt: DateTime!
}
type Query {
posts: [Post!]!
post(id: Int!): Post
}
input CreatePostInput {
title: String!
body: String!
}
input UpdatePostInput {
title: String
body: String
}
type Mutation {
createPost(input: CreatePostInput!): Post!
updatePost(id: Int!, input: UpdatePostInput!): Post!
deletePost(id: Int!): Post!
}
`

View File

@@ -1,35 +1,39 @@
export const schema = gql`
type User {
id: Int!
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: Int!): User
user(id: String!): User
}
input CreateUserInput {
userName: String!
email: String!
# issuer: String!
image: String
bio: String
}
input UpdateUserInput {
userName: String
email: String
# issuer: String
image: String
bio: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: Int!, input: UpdateUserInput!): User!
deleteUser(id: Int!): 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

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

View File

@@ -1,24 +0,0 @@
import { UserInputError } from '@redwoodjs/api'
import { db } from 'src/lib/db'
const validate = (input) => {
if (input.email && !input.email.match(/[^@]+@[^.]+\..+/)) {
throw new UserInputError("Can't create new contact", {
messages: {
email: ['is not formatted like an email address'],
},
})
}
}
export const contacts = () => {
return db.contact.findMany()
}
export const createContact = ({ input }) => {
validate(input)
return db.contact.create({ data: input })
}

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

@@ -1,4 +1,6 @@
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()
@@ -10,15 +12,15 @@ export const part = ({ id }) => {
})
}
export const createPart = ({ input }) => {
export const createPart = async ({ input }) => {
return db.part.create({
data: input,
data: foreignKeyReplacement(input),
})
}
export const updatePart = ({ id, input }) => {
return db.part.update({
data: input,
data: foreignKeyReplacement(input),
where: { id },
})
}
@@ -28,3 +30,11 @@ export const deletePart = ({ id }) => {
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

@@ -1,34 +0,0 @@
import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth'
export const posts = () => {
return db.post.findMany()
}
export const post = ({ id }) => {
return db.post.findOne({
where: { id },
})
}
export const createPost = ({ input }) => {
requireAuth()
return db.post.create({
data: input,
})
}
export const updatePost = ({ id, input }) => {
requireAuth()
return db.post.update({
data: input,
where: { id },
})
}
export const deletePost = ({ id }) => {
requireAuth()
return db.post.delete({
where: { id },
})
}

View File

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

View File

@@ -1,31 +1,22 @@
import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth'
export const users = () => {
requireAuth({ role: 'admin' })
return db.user.findMany()
}
export const user = ({ id }) => {
requireAuth()
return db.user.findOne({
where: { id },
})
}
export const createUser = ({ input }) => {
requireAuth({ role: 'admin' })
return createUserInsecure({input})
}
export const createUserInsecure = ({ input }) => {
return db.user.create({
data: input,
})
}
export const updateUser = ({ id, input }) => {
requireAuth()
return db.user.update({
data: input,
where: { id },
@@ -33,8 +24,15 @@ export const updateUser = ({ id, input }) => {
}
export const deleteUser = ({ id }) => {
requireAuth({ role: 'admin' })
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

@@ -12,30 +12,24 @@ import { Router, Route, Private } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
{/* TODO add add min role to users and users/new */}
<Private unauthenticated="home" role="admin">
<Route path="/users" page={UsersPage} name="users" />
<Route path="/users/new" page={NewUserPage} name="newUser" />
</Private>
<Private unauthenticated="home">
<Route path="/users/{id:Int}/edit" page={EditUserPage} name="editUser" />
</Private>
<Route path="/users/{id:Int}" page={UserPage} name="user" />
<Route path="/contact" page={ContactPage} name="contact" />
<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:Int}/edit" page={EditPartPage} name="editPart" />
<Route path="/parts/{id:Int}/ide" page={IdePartPage} name="partIde" />
<Route path="/parts/{id:Int}" page={PartPage} name="part" />
<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="/blog-post/{id:Int}" page={BlogPostPage} name="blogPost" />
<Private unauthenticated="home">
<Route path="/admin/posts/new" page={NewPostPage} name="newPost" />
<Route path="/admin/posts/{id:Int}/edit" page={EditPostPage} name="editPost" />
<Route path="/admin/posts/{id:Int}" page={PostPage} name="post" />
<Route path="/admin/posts" page={PostsPage} name="posts" />
</Private>
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={PartsPage} name="home" />
<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

@@ -1,16 +0,0 @@
import { Link, routes } from '@redwoodjs/router'
const BlogPost = ({ post }) => {
return (
<article>
<header>
<h2>
<Link to={routes.blogPost({ id: post.id })}>{post.title}</Link>
</h2>
</header>
<div>{post.body}</div>
</article>
)
}
export default BlogPost

View File

@@ -1,7 +0,0 @@
import BlogPost from './BlogPost'
export const generated = () => {
return <BlogPost />
}
export default { title: 'Components/BlogPost' }

View File

@@ -1,11 +0,0 @@
import { render } from '@redwoodjs/testing'
import BlogPost from './BlogPost'
describe('BlogPost', () => {
it('renders successfully', () => {
expect(() => {
render(<BlogPost />)
}).not.toThrow()
})
})

View File

@@ -1,22 +0,0 @@
import BlogPost from 'src/components/BlogPost'
export const QUERY = gql`
query BlogPostQuery($id: Int!) {
post(id: $id) {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ post }) => {
return <BlogPost post={post} />
}

View File

@@ -1,6 +0,0 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
blogPost: {
id: 42,
},
})

View File

@@ -1,20 +0,0 @@
import { Loading, Empty, Failure, Success } from './BlogPostCell'
import { standard } from './BlogPostCell.mock'
export const loading = () => {
return Loading ? <Loading /> : null
}
export const empty = () => {
return Empty ? <Empty /> : null
}
export const failure = () => {
return Failure ? <Failure error={new Error('Oh no')} /> : null
}
export const success = () => {
return Success ? <Success {...standard()} /> : null
}
export default { title: 'Cells/BlogPostCell' }

View File

@@ -1,26 +0,0 @@
import { render, screen } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './BlogPostCell'
import { standard } from './BlogPostCell.mock'
describe('BlogPostCell', () => {
test('Loading renders successfully', () => {
render(<Loading />)
// Use screen.debug() to see output
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
test('Empty renders successfully', async () => {
render(<Empty />)
expect(screen.getByText('Empty')).toBeInTheDocument()
})
test('Failure renders successfully', async () => {
render(<Failure error={new Error('Oh no')} />)
expect(screen.getByText(/Oh no/i)).toBeInTheDocument()
})
test('Success renders successfully', async () => {
render(<Success blogPost={standard().blogPost} />)
expect(screen.getByText(/42/i)).toBeInTheDocument()
})
})

View File

@@ -1,23 +0,0 @@
import { Link, routes } from '@redwoodjs/router'
import BlogPost from 'src/components/BlogPost'
export const QUERY = gql`
query BlogPostsQuery {
posts {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ posts }) => {
return posts.map((post) => <BlogPost key={post.id} post={post} />)
}

View File

@@ -1,6 +0,0 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
blogPosts: {
id: 42,
},
})

View File

@@ -1,20 +0,0 @@
import { Loading, Empty, Failure, Success } from './BlogPostsCell'
import { standard } from './BlogPostsCell.mock'
export const loading = () => {
return Loading ? <Loading /> : null
}
export const empty = () => {
return Empty ? <Empty /> : null
}
export const failure = () => {
return Failure ? <Failure error={new Error('Oh no')} /> : null
}
export const success = () => {
return Success ? <Success {...standard()} /> : null
}
export default { title: 'Cells/BlogPostsCell' }

View File

@@ -1,26 +0,0 @@
import { render, screen } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './BlogPostsCell'
import { standard } from './BlogPostsCell.mock'
describe('BlogPostsCell', () => {
test('Loading renders successfully', () => {
render(<Loading />)
// Use screen.debug() to see output
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
test('Empty renders successfully', async () => {
render(<Empty />)
expect(screen.getByText('Empty')).toBeInTheDocument()
})
test('Failure renders successfully', async () => {
render(<Failure error={new Error('Oh no')} />)
expect(screen.getByText(/Oh no/i)).toBeInTheDocument()
})
test('Success renders successfully', async () => {
render(<Success blogPosts={standard().blogPosts} />)
expect(screen.getByText(/42/i)).toBeInTheDocument()
})
})

View File

@@ -1,9 +1,9 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
const DELETE_POST_MUTATION = gql`
mutation DeletePostMutation($id: Int!) {
deletePost(id: $id) {
const DELETE_COMMENT_MUTATION = gql`
mutation DeleteCommentMutation($id: String!) {
deleteComment(id: $id) {
id
}
}
@@ -29,18 +29,18 @@ const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const Post = ({ post }) => {
const Comment = ({ comment }) => {
const { addMessage } = useFlash()
const [deletePost] = useMutation(DELETE_POST_MUTATION, {
const [deleteComment] = useMutation(DELETE_COMMENT_MUTATION, {
onCompleted: () => {
navigate(routes.posts())
addMessage('Post deleted.', { classes: 'rw-flash-success' })
navigate(routes.comments())
addMessage('Comment deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete post ' + id + '?')) {
deletePost({ variables: { id } })
if (confirm('Are you sure you want to delete comment ' + id + '?')) {
deleteComment({ variables: { id } })
}
}
@@ -49,33 +49,41 @@ const Post = ({ post }) => {
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Post {post.id} Detail
Comment {comment.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{post.id}</td>
<td>{comment.id}</td>
</tr>
<tr>
<th>Title</th>
<td>{post.title}</td>
<th>Text</th>
<td>{comment.text}</td>
</tr>
<tr>
<th>Body</th>
<td>{post.body}</td>
<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(post.createdAt)}</td>
<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.editPost({ id: post.id })}
to={routes.editComment({ id: comment.id })}
className="rw-button rw-button-blue"
>
Edit
@@ -83,7 +91,7 @@ const Post = ({ post }) => {
<a
href="#"
className="rw-button rw-button-red"
onClick={() => onDeleteClick(post.id)}
onClick={() => onDeleteClick(comment.id)}
>
Delete
</a>
@@ -92,4 +100,4 @@ const Post = ({ post }) => {
)
}
export default Post
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

@@ -7,9 +7,9 @@ import {
Submit,
} from '@redwoodjs/forms'
const PostForm = (props) => {
const CommentForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.post?.id)
props.onSave(data, props?.comment?.id)
}
return (
@@ -23,36 +23,52 @@ const PostForm = (props) => {
/>
<Label
name="title"
name="text"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Title
Text
</Label>
<TextField
name="title"
defaultValue={props.post?.title}
name="text"
defaultValue={props.comment?.text}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="title" className="rw-field-error" />
<FieldError name="text" className="rw-field-error" />
<Label
name="body"
name="userId"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Body
User id
</Label>
<TextField
name="body"
defaultValue={props.post?.body}
name="userId"
defaultValue={props.comment?.userId}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="body" className="rw-field-error" />
<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">
@@ -64,4 +80,4 @@ const PostForm = (props) => {
)
}
export default PostForm
export default CommentForm

View File

@@ -1,9 +1,9 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_POST_MUTATION = gql`
mutation DeletePostMutation($id: Int!) {
deletePost(id: $id) {
const DELETE_COMMENT_MUTATION = gql`
mutation DeleteCommentMutation($id: String!) {
deleteComment(id: $id) {
id
}
}
@@ -35,17 +35,17 @@ const checkboxInputTag = (checked) => {
return <input type="checkbox" checked={checked} disabled />
}
const PostsList = ({ posts }) => {
const CommentsList = ({ comments }) => {
const { addMessage } = useFlash()
const [deletePost] = useMutation(DELETE_POST_MUTATION, {
const [deleteComment] = useMutation(DELETE_COMMENT_MUTATION, {
onCompleted: () => {
addMessage('Post deleted.', { classes: 'rw-flash-success' })
addMessage('Comment deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete post ' + id + '?')) {
deletePost({ variables: { id }, refetchQueries: ['POSTS'] })
if (confirm('Are you sure you want to delete comment ' + id + '?')) {
deleteComment({ variables: { id }, refetchQueries: ['COMMENTS'] })
}
}
@@ -55,40 +55,44 @@ const PostsList = ({ posts }) => {
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Body</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>
{posts.map((post) => (
<tr key={post.id}>
<td>{truncate(post.id)}</td>
<td>{truncate(post.title)}</td>
<td>{truncate(post.body)}</td>
<td>{timeTag(post.createdAt)}</td>
{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.post({ id: post.id })}
title={'Show post ' + post.id + ' detail'}
to={routes.comment({ id: comment.id })}
title={'Show comment ' + comment.id + ' detail'}
className="rw-button rw-button-small"
>
Show
</Link>
<Link
to={routes.editPost({ id: post.id })}
title={'Edit post ' + post.id}
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 post ' + post.id}
title={'Delete comment ' + comment.id}
className="rw-button rw-button-small rw-button-red"
onClick={() => onDeleteClick(post.id)}
onClick={() => onDeleteClick(comment.id)}
>
Delete
</a>
@@ -102,4 +106,4 @@ const PostsList = ({ posts }) => {
)
}
export default PostsList
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

@@ -1,8 +1,9 @@
import { useMutation } from '@redwoodjs/web'
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: Int!) {
query FIND_PART_BY_ID($id: String!) {
part: part(id: $id) {
id
title
@@ -10,14 +11,15 @@ export const QUERY = gql`
code
mainImage
createdAt
updatedAt
userId
}
}
`
const UPDATE_PART_MUTATION = gql`
mutation UpdatePartMutation($id: Int!, $input: UpdatePartInput!) {
mutation UpdatePartMutation($id: String!, $input: UpdatePartInput!) {
updatePart(id: $id, input: $input) {
id
code
}
}
`
@@ -25,11 +27,26 @@ const UPDATE_PART_MUTATION = gql`
export const Loading = () => <div>Loading...</div>
export const Success = ({ part }) => {
const [updatePart, { loading, error }] = useMutation(UPDATE_PART_MUTATION)
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 } })
const onSave = (input, id) => {
updatePart({ variables: { id, input } })
}
return (
<PartForm part={part} onSave={onSave} error={error} loading={loading} />
<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

@@ -1,48 +0,0 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PostForm from 'src/components/PostForm'
export const QUERY = gql`
query FIND_POST_BY_ID($id: Int!) {
post: post(id: $id) {
id
title
body
createdAt
}
}
`
const UPDATE_POST_MUTATION = gql`
mutation UpdatePostMutation($id: Int!, $input: UpdatePostInput!) {
updatePost(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ post }) => {
const { addMessage } = useFlash()
const [updatePost, { loading, error }] = useMutation(UPDATE_POST_MUTATION, {
onCompleted: () => {
navigate(routes.posts())
addMessage('Post updated.', { classes: 'rw-flash-success' })
},
})
const onSave = (input, id) => {
updatePost({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">Edit Post {post.id}</h2>
</header>
<div className="rw-segment-main">
<PostForm post={post} onSave={onSave} error={error} loading={loading} />
</div>
</div>
)
}

View File

@@ -3,9 +3,10 @@ import { navigate, routes } from '@redwoodjs/router'
import UserForm from 'src/components/UserForm'
export const QUERY = gql`
query FIND_USER_BY_ID($id: Int!) {
query FIND_USER_BY_ID($id: String!) {
user: user(id: $id) {
id
userName
email
createdAt
updatedAt
@@ -15,7 +16,7 @@ export const QUERY = gql`
}
`
const UPDATE_USER_MUTATION = gql`
mutation UpdateUserMutation($id: Int!, $input: UpdateUserInput!) {
mutation UpdateUserMutation($id: String!, $input: UpdateUserInput!) {
updateUser(id: $id, input: $input) {
id
}

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,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

@@ -1,38 +0,0 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import PostForm from 'src/components/PostForm'
const CREATE_POST_MUTATION = gql`
mutation CreatePostMutation($input: CreatePostInput!) {
createPost(input: $input) {
id
}
}
`
const NewPost = () => {
const { addMessage } = useFlash()
const [createPost, { loading, error }] = useMutation(CREATE_POST_MUTATION, {
onCompleted: () => {
navigate(routes.posts())
addMessage('Post created.', { classes: 'rw-flash-success' })
},
})
const onSave = (input) => {
createPost({ variables: { input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">New Post</h2>
</header>
<div className="rw-segment-main">
<PostForm onSave={onSave} loading={loading} error={error} />
</div>
</div>
)
}
export default NewPost

View File

@@ -1,23 +1,35 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
import { initialize } from 'src/cascade/js/MainPage/CascadeMain'
import { useEffect, useState } from 'react'
const DELETE_PART_MUTATION = gql`
mutation DeletePartMutation($id: Int!) {
mutation DeletePartMutation($id: String!) {
deletePart(id: $id) {
id
}
}
`
const Part = ({ part, saveCode, loading, error}) => {
const [code, setCode] = useState(part.code)
useEffect(() => {
const sickCallback = (code) => setCode(code)
initialize(sickCallback, part.code)
}, [])
const hasChanges = code !== part.code
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: () => {
@@ -42,6 +54,10 @@ const Part = ({ part, saveCode, loading, error}) => {
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Id</th>
<td>{part.id}</td>
</tr>
<tr>
<th>Title</th>
<td>{part.title}</td>
@@ -50,59 +66,44 @@ const Part = ({ part, saveCode, loading, error}) => {
<th>Description</th>
<td>{part.description}</td>
</tr>
{/* <tr>
<tr>
<th>Code</th>
<td>{part.code}</td>
</tr> */}
{/* <tr>
</tr>
<tr>
<th>Main image</th>
<td>{part.mainImage}</td>
</tr> */}
{/* <tr>
</tr>
<tr>
<th>Created at</th>
<td>{timeTag(part.createdAt)}</td>
</tr> */}
</tr>
<tr>
<th>Updated at</th>
<td>{timeTag(part.updatedAt)}</td>
</tr>
<tr>
<th>User id</th>
<td>{part.userId}</td>
</tr>
</tbody>
</table>
<img src={part.mainImage} />
</div>
<nav className="rw-button-group">
{loading && 'Loading...'}
{hasChanges && !loading && <button onClick={() => saveCode({code}, part.id)} className="rw-button rw-button-blue">
Save Changes
</button>}
<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>
<div>
<div id="topnav" className="topnav">
<a href="https://github.com/zalo/CascadeStudio">Cascade Studio 0.0.6</a>
<a href="#" id="main-proj-button" title="Sets this project to save in local storage." onClick={() => makeMainProject()}>Make Main Project</a>
<a href="#" title="Save Project to .json" onClick={() => saveProject()}>Save Project</a>
<label htmlFor="project-file" title="Load Project from .json">Load Project
<input
id="project-file"
name="project-file"
type="file"
accept=".json"
style={{display:'none'}}
onInput={() => loadProject()}
/>
</label>
<a href="#" onClick={() => threejsViewport.saveShapeSTEP()}>Save STEP</a>
<a href="#" onClick={() => threejsViewport.saveShapeSTL()}>Save STL</a>
<a href="#" onClick={() => threejsViewport.saveShapeOBJ()}>Save OBJ</a>
<label htmlFor="files" title="Import STEP, IGES, or (ASCII) STL from File">Import STEP/IGES/STL
<input id="files" name="files" type="file" accept=".iges,.step,.igs,.stp,.stl" multiple style={{display: 'none'}} onInput={ () =>loadFiles()}/>
</label>
<a href="#" title="Clears the external step/iges/stl files stored in the project." onClick={() => clearExternalFiles()}>Clear Imported Files</a>
<a href="" title="Resets the project and localstorage." onClick={() => {
window.localStorage.clear();
window.history.replaceState({}, 'Cascade Studio','?')
}}>Reset Project</a>
</div>
<div id="cascade-container" style={{height:'auto'}}>
</div>
<footer>footer</footer>
</div>
</>
)
}

View File

@@ -1,32 +1,24 @@
import Part from 'src/components/Part'
import {QUERY as reExportQuery} from 'src/components/EditPartCell'
import Editor from "rich-markdown-editor";
export const QUERY = reExportQuery
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>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Empty = () => <div>Part not found</div>
export const Success = ({ part }) => {
console.log(part)
return (
<>
<div className="max-w-7xl mx-auto">
<div className="flex justify-center">
<img src={part.mainImage} />
</div>
<div className="bg-white p-8 my-12 min-h-md">
<h2 className="text-4xl py-4">{part.title}</h2>
<Editor
className="markdown-overrides"
defaultValue={part.description}
readOnly
/>
</div>
</div>
</>
)
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

@@ -6,36 +6,14 @@ import {
TextField,
Submit,
} from '@redwoodjs/forms'
import { useState } from 'react';
import { navigate, routes } from '@redwoodjs/router'
import { useFlash } from '@redwoodjs/web'
import ImageUploader from './ImageUploader.js'
import Editor from "rich-markdown-editor";
const PartForm = (props) => {
const { addMessage } = useFlash()
const [description, setDescription] = useState(props?.part?.description)
const [imageUrl, setImageUrl] = useState(props?.part?.mainImage)
const onSubmit = async (data, e) => {
await props.onSave({
...data,
description,
mainImage: imageUrl
}, props?.part?.id)
const shouldOpenIde = e?.nativeEvent?.submitter?.dataset?.openIde
if(shouldOpenIde) {
navigate(routes.partIde({id: props?.part?.id}))
} else {
navigate(routes.part({id: props?.part?.id}))
}
addMessage('Part updated.', { classes: 'rw-flash-success' })
const onSubmit = (data) => {
props.onSave(data, props?.part?.id)
}
return (
<div className="max-w-7xl mx-auto mt-10">
<div className="rw-form-wrapper">
<Form onSubmit={onSubmit} error={props.error}>
<FormError
error={props.error}
@@ -46,7 +24,7 @@ const PartForm = (props) => {
<Label
name="title"
className="p-0"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Title
@@ -60,30 +38,74 @@ const PartForm = (props) => {
/>
<FieldError name="title" className="rw-field-error" />
<ImageUploader onImageUpload={({cloudinaryPublicId}) => setImageUrl(cloudinaryPublicId)} />
<Label
name="description"
className="p-0"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Description
</Label>
<div name="description" className="markdown-overrides bg-white p-12 my-10 min-h-md">
<Editor
defaultValue={props.part?.description}
onChange={(valueFn) => setDescription(valueFn())}
/>
</div>
<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>
<Submit disabled={props.loading} data-open-ide={true} className="rw-button rw-button-blue">
Save and open IDE
</Submit>
</div>
</Form>
</div>

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

@@ -1,11 +1,8 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
import { Image as CloudinaryImage } from 'cloudinary-react'
import avatar from 'src/assets/harold.jpg'
const DELETE_PART_MUTATION = gql`
mutation DeletePartMutation($id: Int!) {
mutation DeletePartMutation($id: String!) {
deletePart(id: $id) {
id
}
@@ -53,35 +50,62 @@ const PartsList = ({ parts }) => {
}
return (
<div className="max-w-xs sm:max-w-sm md:max-w-2xl lg:max-w-5xl xl:max-w-6xl mx-auto grid gap-8 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid- cols-4">
{parts.map((part) => {
return (
<Link
to={routes.part({ id: part.id })}
title={'Show part ' + part.id + ' detail'}
key={part.id}
className="relative bg-gray-900 rounded-t-2xl"
>
<div className="rounded-t-2xl bg-gray-900">
<div className="flex items-center p-2 text-indigo-200">
<div className="h-full absolute inset-0 text-6xl flex items-center justify-center text-indigo-700" ><span>?</span></div>
<div className="mr-4">
<img src={avatar} className="rounded-full h-10 w-10" />
</div>
<h3>{part.title}</h3>
</div>
<div className="relative z-10">
<CloudinaryImage
className="object-cover w-full rounded shadow"
cloudName="irevdev"
publicId={part.mainImage}
width="300"
crop="scale"
/>
</div>
</div>
</Link>
)})}
<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>
)
}

View File

@@ -11,6 +11,8 @@ export const QUERY = gql`
code
mainImage
createdAt
updatedAt
userId
}
}
`

View File

@@ -1,20 +0,0 @@
import Post from 'src/components/Post'
export const QUERY = gql`
query FIND_POST_BY_ID($id: Int!) {
post: post(id: $id) {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Post not found</div>
export const Success = ({ post }) => {
return <Post post={post} />
}

View File

@@ -1,31 +0,0 @@
import { Link, routes } from '@redwoodjs/router'
import Posts from 'src/components/Posts'
export const QUERY = gql`
query POSTS {
posts {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => {
return (
<div className="rw-text-center">
{'No posts yet. '}
<Link to={routes.newPost()} className="rw-link">
{'Create one?'}
</Link>
</div>
)
}
export const Success = ({ posts }) => {
return <Posts posts={posts} />
}

View File

@@ -1,8 +1,8 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes, navigate } from '@redwoodjs/router'
import { Image as CloudinaryImage } from 'cloudinary-react'
const DELETE_USER_MUTATION = gql`
mutation DeleteUserMutation($id: Int!) {
mutation DeleteUserMutation($id: String!) {
deleteUser(id: $id) {
id
}
@@ -58,6 +58,10 @@ const User = ({ user }) => {
<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>
@@ -72,13 +76,7 @@ const User = ({ user }) => {
</tr>
<tr>
<th>Image</th>
<td><CloudinaryImage
className="object-cover w-full rounded shadow"
cloudName="irevdev"
publicId={user.image}
width="300"
crop="scale"
/></td>
<td>{user.image}</td>
</tr>
<tr>
<th>Bio</th>

View File

@@ -1,9 +1,10 @@
import User from 'src/components/User'
export const QUERY = gql`
query FIND_USER_BY_ID($id: Int!) {
query FIND_USER_BY_ID($id: String!) {
user: user(id: $id) {
id
userName
email
createdAt
updatedAt

View File

@@ -6,21 +6,10 @@ import {
TextField,
Submit,
} from '@redwoodjs/forms'
import { useState } from 'react';
import { navigate, routes } from '@redwoodjs/router'
import { useFlash } from '@redwoodjs/web'
import ImageUploader from '../PartForm/ImageUploader'
const UserForm = (props) => {
const { addMessage } = useFlash()
// const [bio, setBio] = useState(props?.user?.bio)
const [imageUrl, setImageUrl] = useState(props?.user?.image)
const onSubmit = async (data, e) => {
await props.onSave({
...data,
image: imageUrl
}, props?.user?.id)
addMessage('User updated.', { classes: 'rw-flash-success' })
const UserForm = (props) => {
const onSubmit = (data) => {
props.onSave(data, props?.user?.id)
}
return (
@@ -33,6 +22,22 @@ const UserForm = (props) => {
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"
@@ -48,7 +53,22 @@ const UserForm = (props) => {
validation={{ required: true }}
/>
<FieldError name="email" className="rw-field-error" />
<ImageUploader onImageUpload={({cloudinaryPublicId}) => setImageUrl(cloudinaryPublicId)} />
<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"

View File

@@ -1,16 +0,0 @@
function UserPart({ userName, partName }) {
return (
<h3 className="text-xl font-roboto">
<div className="w-1 inline-block text-indigo-800 bg-indigo-800 mr-2">.</div>
<span className="text-gray-500">
{userName}
</span>
<div className="w-1 inline-block bg-gray-400 text-gray-400 mx-3 transform -skew-x-20" >.</div>
<span className="text-indigo-800">
{partName}
</span>
</h3>
)
}
export default UserPart

View File

@@ -2,7 +2,7 @@ import { useMutation, useFlash } from '@redwoodjs/web'
import { Link, routes } from '@redwoodjs/router'
const DELETE_USER_MUTATION = gql`
mutation DeleteUserMutation($id: Int!) {
mutation DeleteUserMutation($id: String!) {
deleteUser(id: $id) {
id
}
@@ -55,6 +55,7 @@ const UsersList = ({ users }) => {
<thead>
<tr>
<th>Id</th>
<th>User name</th>
<th>Email</th>
<th>Created at</th>
<th>Updated at</th>
@@ -67,6 +68,7 @@ const UsersList = ({ users }) => {
{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>

View File

@@ -6,6 +6,7 @@ export const QUERY = gql`
query USERS {
users {
id
userName
email
createdAt
updatedAt

View File

@@ -38,7 +38,7 @@
}
}
}
coolGuy()
// coolGuy()
</script>
</head>
<body>

View File

@@ -1,24 +0,0 @@
import { Link, routes } from '@redwoodjs/router'
const BlogLayout = ({ children }) => {
return (
<>
<header>
<h1>Redwood Blog</h1>
<nav>
<ul>
<li>
<Link to={routes.about()}>About</Link>
</li>
<li>
<Link to={routes.home()}>Home</Link>
</li>
</ul>
</nav>
</header>
<main>{children}</main>
</>
)
}
export default BlogLayout

View File

@@ -1,7 +0,0 @@
import BlogLayout from './BlogLayout'
export const generated = () => {
return <BlogLayout />
}
export default { title: 'Layouts/BlogLayout' }

View File

@@ -1,11 +0,0 @@
import { render } from '@redwoodjs/testing'
import BlogLayout from './BlogLayout'
describe('BlogLayout', () => {
it('renders successfully', () => {
expect(() => {
render(<BlogLayout />)
}).not.toThrow()
})
})

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

@@ -1,11 +0,0 @@
const PartsLayout = (props) => {
return (
<div className="mt-8">
<main>{props.children}</main>
</div>
)
}
export default PartsLayout

View File

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

View File

@@ -1,19 +0,0 @@
import { Link, routes } from '@redwoodjs/router'
import BlogLayout from 'src/layouts/BlogLayout'
const AboutPage = () => {
return (
<BlogLayout>
<h1>AboutPage</h1>
<p>
Find me in <tt>./web/src/pages/AboutPage/AboutPage.js</tt>
</p>
<p>
My default route is named <tt>about</tt>, link to me with `
<Link to={routes.about()}>About</Link>`
</p>
</BlogLayout>
)
}
export default AboutPage

View File

@@ -1,7 +0,0 @@
import AboutPage from './AboutPage'
export const generated = () => {
return <AboutPage />
}
export default { title: 'Pages/AboutPage' }

View File

@@ -1,11 +0,0 @@
import { render } from '@redwoodjs/testing'
import AboutPage from './AboutPage'
describe('AboutPage', () => {
it('renders successfully', () => {
expect(() => {
render(<AboutPage />)
}).not.toThrow()
})
})

View File

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

View File

@@ -1,7 +0,0 @@
import BlogPostPage from './BlogPostPage'
export const generated = () => {
return <BlogPostPage />
}
export default { title: 'Pages/BlogPostPage' }

Some files were not shown because too many files have changed in this diff Show More