add parts data

This commit is contained in:
Kurt Hutten
2020-10-11 19:41:48 +11:00
parent f1dc6c28e4
commit b3456860d2
34 changed files with 1146 additions and 11 deletions

View File

@@ -0,0 +1,52 @@
# 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

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,105 @@
{
"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

@@ -0,0 +1,65 @@
# 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

@@ -0,0 +1,29 @@
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

@@ -0,0 +1,37 @@
{
"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

@@ -0,0 +1,69 @@
# 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

@@ -0,0 +1,29 @@
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

@@ -0,0 +1,19 @@
{
"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,3 +1,6 @@
# Prisma Migrate lockfile v1
20201009213512-create-posts
20201009213512-create-posts
20201011043647-create-parts
20201011052155-add-code-to-part
20201011082558-add-code-not-needed-upon-create

View File

@@ -16,3 +16,14 @@ model Post {
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

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

View File

@@ -0,0 +1,30 @@
import { db } from 'src/lib/db'
export const parts = () => {
return db.part.findMany()
}
export const part = ({ id }) => {
return db.part.findOne({
where: { id },
})
}
export const createPart = ({ input }) => {
return db.part.create({
data: input,
})
}
export const updatePart = ({ id, input }) => {
return db.part.update({
data: input,
where: { id },
})
}
export const deletePart = ({ id }) => {
return db.part.delete({
where: { id },
})
}

View File

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

View File

@@ -12,13 +12,17 @@ import { Router, Route } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/parts/new" page={NewPartPage} name="newPart" />
<Route path="/parts/{id:Int}/edit" page={EditPartPage} name="editPart" />
<Route path="/parts/{id:Int}" page={PartPage} name="part" />
<Route path="/parts" page={PartsPage} name="parts" />
<Route path="/blog-post/{id:Int}" page={BlogPostPage} name="blogPost" />
<Route path="/posts/new" page={NewPostPage} name="newPost" />
<Route path="/posts/{id:Int}/edit" page={EditPostPage} name="editPost" />
<Route path="/posts/{id:Int}" page={PostPage} name="post" />
<Route path="/posts" page={PostsPage} name="posts" />
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={HomePage} name="home" />
<Route path="/" page={PartsPage} name="home" />
<Route notfound page={NotFoundPage} />
</Router>
)

View File

@@ -0,0 +1,51 @@
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!) {
part: part(id: $id) {
id
title
description
code
mainImage
createdAt
}
}
`
const UPDATE_PART_MUTATION = gql`
mutation UpdatePartMutation($id: Int!, $input: UpdatePartInput!) {
updatePart(id: $id, input: $input) {
id
code
}
}
`
export const Loading = () => <div>Loading...</div>
export const Success = ({ part }) => {
const { addMessage } = useFlash()
const [updatePart, { loading, error }] = useMutation(UPDATE_PART_MUTATION, {
onCompleted: () => {
navigate(routes.parts())
addMessage('Part updated.', { classes: 'rw-flash-success' })
},
})
const onSave = (input, id) => {
updatePart({ variables: { id, input } })
}
return (
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">Edit Part {part.id}</h2>
</header>
<div className="rw-segment-main">
<PartForm part={part} onSave={onSave} error={error} loading={loading} />
</div>
</div>
)
}

View File

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

View File

@@ -0,0 +1,110 @@
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!) {
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 { addMessage } = useFlash()
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
onCompleted: () => {
navigate(routes.parts())
addMessage('Part deleted.', { classes: 'rw-flash-success' })
},
})
const onDeleteClick = (id) => {
if (confirm('Are you sure you want to delete part ' + id + '?')) {
deletePart({ variables: { id } })
}
}
return (
<>
<div className="rw-segment">
<header className="rw-segment-header">
<h2 className="rw-heading rw-heading-secondary">
Part {part.id} Detail
</h2>
</header>
<table className="rw-table">
<tbody>
<tr>
<th>Title</th>
<td>{part.title}</td>
</tr>
<tr>
<th>Description</th>
<td>{part.description}</td>
</tr>
{/* <tr>
<th>Code</th>
<td>{part.code}</td>
</tr> */}
{/* <tr>
<th>Main image</th>
<td>{part.mainImage}</td>
</tr> */}
{/* <tr>
<th>Created at</th>
<td>{timeTag(part.createdAt)}</td>
</tr> */}
</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>}
</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>
</>
)
}
export default Part

View File

@@ -0,0 +1,46 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import Part from 'src/components/Part'
export const QUERY = gql`
query FIND_PART_BY_ID($id: Int!) {
part: part(id: $id) {
id
title
description
code
mainImage
createdAt
}
}
`
const UPDATE_PART_MUTATION = gql`
mutation UpdatePartMutation($id: Int!, $input: UpdatePartInput!) {
updatePart(id: $id, input: $input) {
id
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Part not found</div>
export const Success = ({ part }) => {
const { addMessage } = useFlash()
const [updatePart, { loading, error }] = useMutation(UPDATE_PART_MUTATION, {
onCompleted: () => {
// navigate(routes.part({id: updatePart.id}))
addMessage('Part updated.', { classes: 'rw-flash-success' })
},
})
console.log({updatePart})
const saveCode = (input, id) => {
console.log(id, input, 'wowow')
updatePart({ variables: { id, input } })
}
return <Part part={{...part, code: part.code}} saveCode={saveCode} loading={loading} error={error} />
}

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@
// Begins loading the CAD Kernel Web Worker
if (window.Worker) {
cascadeStudioWorker = new Worker('./src/cascade/js/CADWorker/CascadeStudioMainWorker.js');
cascadeStudioWorker = new Worker('/src/cascade/js/CADWorker/CascadeStudioMainWorker.js');
// Ping Pong Messages Back and Forth based on their registration in messageHandlers
// var messageHandlers = {};
cascadeStudioWorker.onmessage = function (e) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import BlogLayout from 'src/layouts/BlogLayout'
import MainLayout from 'src/layouts/MainLayout'
import BlogPostsCell from 'src/components/BlogPostsCell'
import { initialize } from 'src/cascade/js/MainPage/CascadeMain'
import { useEffect, useState } from 'react'
@@ -35,11 +35,10 @@ const HomePage = () => {
}, [])
return (
<BlogLayout>
<MainLayout>
<div>current code {code}</div>
<BlogPostsCell/>
<div>
<h1 hidden></h1>
<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>
@@ -70,7 +69,7 @@ const HomePage = () => {
</div>
<footer>footer</footer>
</div>
</BlogLayout>
</MainLayout>
)
}

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,14 @@
import MainLayout from 'src/layouts/MainLayout'
import PostsLayout from 'src/layouts/PostsLayout'
import PostsCell from 'src/components/PostsCell'
const PostsPage = () => {
return (
<PostsLayout>
<PostsCell />
</PostsLayout>
<MainLayout>
<PostsLayout>
<PostsCell />
</PostsLayout>
</MainLayout>
)
}