Added TodoApp tutorial example.

This commit is contained in:
Martin Sosic 2020-10-12 15:03:07 +02:00
parent bbe6dae679
commit b480420690
13 changed files with 329 additions and 2 deletions

View File

@ -1,6 +1,6 @@
.PHONY: buildTodoApp
buildTodoApp:
buildTodoApp:
cd todoApp/out && npm install && npm run-script build
.PHONY: deploy

View File

@ -2,7 +2,7 @@ Each example has `src/` and `out/` directory, where `out/` is code generated by
Examples are deployed to https://examples.wasp-lang.dev.
NOTE: Right now, there is only one example, and it is deployed directly to https://examples.wasp-lang.dev.
NOTE: Right now, only todoApp/ is deployed, and it is deployed directly to https://examples.wasp-lang.dev.
Plan is, when we will have more examples, to deploy them next to each other, each in its own directory, so they would be accessed as https://examples.wasp-lang.dev/todoApp and so on.
But, for that we need support in Wasp for the scenario where frontend is not hosted at server root, which we don't support yet.

1
examples/tutorials/TodoApp/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.wasp/

View File

@ -0,0 +1 @@
File marking the root of Wasp project.

View File

@ -0,0 +1,19 @@
import React, { useEffect, useState } from 'react'
import Clock from 'react-clock'
import 'react-clock/dist/Clock.css'
export default () => {
const [time, setTime] = useState(new Date())
useEffect(() => {
const interval = setInterval(() => setTime(new Date()), 1000)
return () => clearInterval(interval)
}, [])
return (
<div style={{ display: 'flex' }}>
<Clock value={time} />
<Clock value={new Date(time.getTime() + 60 * 60000)} />
</div>
)
}

View File

@ -0,0 +1,81 @@
import React, { useState } from 'react'
import { useQuery } from '@wasp/queries'
import getTasks from '@wasp/queries/getTasks'
import createTask from '@wasp/actions/createTask'
import updateTask from '@wasp/actions/updateTask'
import Clocks from './Clocks'
const MainPage = () => {
const { data: tasks, isFetching, error } = useQuery(getTasks)
return (
<div>
<NewTaskForm />
{tasks && <TasksList tasks={tasks} />}
<p> <Clocks /> </p>
{isFetching && 'Fetching...'}
{error && 'Error: ' + error}
</div>
)
}
const Task = (props) => {
const handleIsDoneChange = async (event) => {
try {
await updateTask({
taskId: props.task.id,
data: { isDone: event.target.checked }
})
} catch (error) {
window.alert('Error while updating task: ' + error.message)
}
}
return (
<div>
<input
type='checkbox' id={props.task.id}
checked={props.task.isDone} readonly
onChange={handleIsDoneChange}
/>
{props.task.description}
</div>
)
}
const TasksList = (props) => {
if (!props.tasks?.length) return 'No tasks'
return props.tasks.map((task, idx) => <Task task={task} key={idx} />)
}
const NewTaskForm = (props) => {
const defaultDescription = ''
const [description, setDescription] = useState(defaultDescription)
const handleSubmit = async (event) => {
event.preventDefault()
try {
await createTask({ description })
setDescription(defaultDescription)
} catch (err) {
window.alert('Error: ' + err.message)
}
}
return (
<form onSubmit={handleSubmit}>
<input
type='text'
value={description}
onChange={e => setDescription(e.target.value)}
/>
<input type='submit' value='Create task' />
</form>
)
}
export default MainPage

View File

@ -0,0 +1,12 @@
export const createTask = async ({ description }, context) => {
return context.entities.Task.create({
data: { description }
})
}
export const updateTask = async (args, context) => {
return context.entities.Task.update({
where: { id: args.taskId },
data: args.data
})
}

View File

@ -0,0 +1,4 @@
export const getTasks = async (args, context) => {
return context.entities.Task.findMany({})
}

View File

@ -0,0 +1,33 @@
app TodoApp {
title: "TodoApp"
}
route "/" -> page Main
page Main {
component: import Main from "@ext/MainPage.js"
}
entityPSL Task {=psl
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
psl=}
query getTasks {
fn: import { getTasks } from "@ext/queries.js",
entities: [Task]
}
action createTask {
fn: import { createTask } from "@ext/actions.js",
entities: [Task]
}
action updateTask {
fn: import { updateTask } from "@ext/actions.js",
entities: [Task]
}
dependencies {=json
"react-clock": "3.0.0"
json=}

View File

@ -0,0 +1,43 @@
# Migration `20201008211343-added-task`
This migration has been generated by Martin Sosic at 10/8/2020, 11:13:43 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "Task" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"description" TEXT NOT NULL,
"isDone" BOOLEAN NOT NULL DEFAULT false
)
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20201008211343-added-task
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,17 @@
+
+datasource db {
+ provider = "sqlite"
+ url = "***"
+}
+
+generator client {
+ provider = "prisma-client-js"
+ output = "../server/node_modules/.prisma/client"
+}
+
+model Task {
+ id Int @id @default(autoincrement())
+ description String
+ isDone Boolean @default(false)
+}
+
```

View File

@ -0,0 +1,17 @@
datasource db {
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
output = "../server/node_modules/.prisma/client"
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
}

View File

@ -0,0 +1,113 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "db"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "db"
},
"argument": "provider",
"value": "\"sqlite\""
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "db"
},
"argument": "url",
"value": "\"***\""
},
{
"tag": "CreateModel",
"model": "Task"
},
{
"tag": "CreateField",
"model": "Task",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Task",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Task",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Task",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateField",
"model": "Task",
"field": "description",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "Task",
"field": "isDone",
"type": "Boolean",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Task",
"field": "isDone"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Task",
"field": "isDone"
},
"directive": "default"
},
"argument": "",
"value": "false"
}
]
}

View File

@ -0,0 +1,3 @@
# Prisma Migrate lockfile v1
20201008211343-added-task