~repos /edge-city
git clone https://pyrossh.dev/repos/edge-city.git
edge-city is a next level meta-framework for react that runs only on edge runtimes
e6b4ebec
—
pyrossh 2 years ago
Add eslint/prettier
- example/.vscode/extensions.json +2 -1
- example/.vscode/settings.json +6 -1
- example/package.json +12 -5
- example/src/components/Counter/Counter.css +1 -1
- example/src/components/Counter/Counter.jsx +5 -7
- example/src/components/Layout/Layout.css +1 -1
- example/src/components/Layout/Layout.jsx +5 -7
- example/src/components/Timer/Timer.jsx +3 -3
- example/src/components/Todo/Todo.css +3 -2
- example/src/components/Todo/Todo.jsx +14 -10
- example/src/init.js +10 -8
- example/src/pages/_404/page.css +4 -3
- example/src/pages/_404/page.jsx +4 -4
- example/src/pages/_500/page.css +4 -3
- example/src/pages/_500/page.jsx +4 -4
- example/src/pages/about/page.css +1 -1
- example/src/pages/about/page.jsx +4 -6
- example/src/pages/app.css +41 -13
- example/src/pages/app.jsx +5 -7
- example/src/pages/page.css +1 -1
- example/src/pages/page.jsx +5 -7
- example/src/pages/page.spec.js +1 -1
- example/src/pages/todos/page.css +4 -4
- example/src/pages/todos/page.jsx +17 -11
- example/src/services/todos.service.js +16 -16
- example/src/services/todos.service.test.js +27 -31
- pnpm-lock.yaml +9 -0
example/.vscode/extensions.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"recommendations": [
|
|
3
|
-
"ms-playwright.playwright"
|
|
3
|
+
"ms-playwright.playwright",
|
|
4
|
+
"esbenp.prettier-vscode"
|
|
4
5
|
]
|
|
5
6
|
}
|
example/.vscode/settings.json
CHANGED
|
@@ -9,5 +9,10 @@
|
|
|
9
9
|
"**/dist": true,
|
|
10
10
|
"**/playwright-report": true,
|
|
11
11
|
"test-results": true
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
14
|
+
"[javascript]": {
|
|
15
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
16
|
+
"editor.formatOnSave": true
|
|
17
|
+
},
|
|
13
18
|
}
|
example/package.json
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
"name": "edge-city-example",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"scripts": {
|
|
5
|
+
"lint": "eslint ./src",
|
|
6
|
+
"fmt": "prettier ./src --write",
|
|
5
7
|
"dev": "edge-city dev -p cloudflare",
|
|
6
8
|
"dev:wrangler": "cd build && wrangler pages dev static",
|
|
7
9
|
"deploy:wrangler": "cd build && wrangler pages deploy static",
|
|
@@ -30,13 +32,17 @@
|
|
|
30
32
|
"eslint": "^8.35.0",
|
|
31
33
|
"eslint-config-react-app": "^7.0.1",
|
|
32
34
|
"postgres": "3.3.4",
|
|
35
|
+
"prettier": "^2.8.8",
|
|
33
36
|
"wrangler": "3.0.1"
|
|
34
37
|
},
|
|
35
38
|
"prettier": {
|
|
36
|
-
"printWidth":
|
|
39
|
+
"printWidth": 100,
|
|
40
|
+
"singleQuote": false,
|
|
41
|
+
"jsxSingleQuote": false,
|
|
42
|
+
"bracketSameLine": false,
|
|
43
|
+
"trailingComma": "all"
|
|
37
44
|
},
|
|
38
45
|
"eslintConfig": {
|
|
39
|
-
"root": true,
|
|
40
46
|
"parserOptions": {
|
|
41
47
|
"ecmaVersion": "latest",
|
|
42
48
|
"sourceType": "module"
|
|
@@ -45,13 +51,14 @@
|
|
|
45
51
|
"eslint:recommended",
|
|
46
52
|
"react-app"
|
|
47
53
|
],
|
|
48
|
-
"ignorePatterns": [
|
|
49
|
-
"build"
|
|
50
|
-
],
|
|
51
54
|
"rules": {
|
|
52
55
|
"react/prop-types": "warn",
|
|
53
56
|
"react/react-in-jsx-scope": "off",
|
|
54
57
|
"no-unused-vars": "warn"
|
|
58
|
+
},
|
|
59
|
+
"globals": {
|
|
60
|
+
"globalThis": false,
|
|
61
|
+
"db": false
|
|
55
62
|
}
|
|
56
63
|
}
|
|
57
64
|
}
|
example/src/components/Counter/Counter.css
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
color: white;
|
|
7
7
|
margin-left: 0.5rem;
|
|
8
8
|
}
|
|
9
|
-
}
|
|
9
|
+
}
|
example/src/components/Counter/Counter.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Button } from
|
|
2
|
+
import { Button } from "react-aria-components";
|
|
3
3
|
import "./Counter.css";
|
|
4
4
|
|
|
5
5
|
const Counter = () => {
|
|
@@ -11,14 +11,12 @@ const Counter = () => {
|
|
|
11
11
|
<Button className="button" onClick={increment}>
|
|
12
12
|
-
|
|
13
13
|
</Button>
|
|
14
|
-
<span className="count">
|
|
14
|
+
<span className="count">{count}</span>
|
|
15
|
-
{count}
|
|
16
|
-
</span>
|
|
17
15
|
<Button className="button" onClick={decrement}>
|
|
18
16
|
+
|
|
19
17
|
</Button>
|
|
20
18
|
</div>
|
|
21
|
-
)
|
|
19
|
+
);
|
|
22
|
-
}
|
|
20
|
+
};
|
|
23
21
|
|
|
24
|
-
export default Counter;
|
|
22
|
+
export default Counter;
|
example/src/components/Layout/Layout.css
CHANGED
|
@@ -24,4 +24,4 @@
|
|
|
24
24
|
border-left: 2px solid #eee;
|
|
25
25
|
min-height: 100vh;
|
|
26
26
|
}
|
|
27
|
-
}
|
|
27
|
+
}
|
example/src/components/Layout/Layout.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { Link } from "edge-city";
|
|
3
3
|
import "./Layout.css";
|
|
4
4
|
|
|
@@ -10,11 +10,9 @@ const Layout = ({ children }) => {
|
|
|
10
10
|
<Link href="/about">About us</Link>
|
|
11
11
|
<Link href="/todos">Todos</Link>
|
|
12
12
|
</div>
|
|
13
|
-
<div className="content">
|
|
13
|
+
<div className="content">{children}</div>
|
|
14
|
-
{children}
|
|
15
|
-
</div>
|
|
16
14
|
</div>
|
|
17
|
-
)
|
|
15
|
+
);
|
|
18
|
-
}
|
|
16
|
+
};
|
|
19
17
|
|
|
20
|
-
export default Layout;
|
|
18
|
+
export default Layout;
|
example/src/components/Timer/Timer.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
2
|
|
|
3
3
|
export default function Timer() {
|
|
4
4
|
const [counter, setCounter] = useState(0);
|
|
@@ -8,11 +8,11 @@ export default function Timer() {
|
|
|
8
8
|
}, 100);
|
|
9
9
|
return () => {
|
|
10
10
|
clearInterval(ref);
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
}, []);
|
|
13
13
|
return (
|
|
14
14
|
<div>
|
|
15
15
|
<p>(This page is interactive while data is loading: {counter})</p>
|
|
16
16
|
</div>
|
|
17
17
|
);
|
|
18
|
-
}
|
|
18
|
+
}
|
example/src/components/Todo/Todo.css
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
display: flex;
|
|
12
12
|
flex: 1;
|
|
13
13
|
flex-direction: column;
|
|
14
|
-
font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",
|
|
14
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
|
15
|
+
"Courier New", monospace;
|
|
15
16
|
margin-left: 0.5rem;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -29,4 +30,4 @@
|
|
|
29
30
|
& .delete-button {
|
|
30
31
|
padding: 0.5rem;
|
|
31
32
|
}
|
|
32
|
-
}
|
|
33
|
+
}
|
example/src/components/Todo/Todo.jsx
CHANGED
|
@@ -19,10 +19,13 @@ const Todo = ({ item, updateMutation, deleteMutation }) => {
|
|
|
19
19
|
setBusy(true);
|
|
20
20
|
save(item, null, item.completed);
|
|
21
21
|
}, [item]);
|
|
22
|
-
const doSaveCompleted = useCallback(
|
|
22
|
+
const doSaveCompleted = useCallback(
|
|
23
|
+
(completed) => {
|
|
23
|
-
|
|
24
|
+
setBusy(true);
|
|
24
|
-
|
|
25
|
+
save(item, item.text, completed);
|
|
26
|
+
},
|
|
25
|
-
|
|
27
|
+
[item],
|
|
28
|
+
);
|
|
26
29
|
return (
|
|
27
30
|
<li className="todo">
|
|
28
31
|
{!editing && (
|
|
@@ -32,16 +35,17 @@ const Todo = ({ item, updateMutation, deleteMutation }) => {
|
|
|
32
35
|
<p>{item.text}</p>
|
|
33
36
|
<p className="timestamp">{item.createdAt}</p>
|
|
34
37
|
</div>
|
|
35
|
-
<button className="edit-button" title="Edit">
|
|
38
|
+
<button className="edit-button" title="Edit">
|
|
39
|
+
✏️
|
|
40
|
+
</button>
|
|
36
|
-
<button class="delete-button" title="Delete">
|
|
41
|
+
<button class="delete-button" title="Delete">
|
|
42
|
+
🗑️
|
|
43
|
+
</button>
|
|
37
44
|
</>
|
|
38
45
|
)}
|
|
39
46
|
{editing && (
|
|
40
47
|
<>
|
|
41
|
-
<input
|
|
42
|
-
|
|
48
|
+
<input class="border rounded w-full py-2 px-3 mr-4" defaultValue={item.text} />
|
|
43
|
-
defaultValue={item.text}
|
|
44
|
-
/>
|
|
45
49
|
<button
|
|
46
50
|
class="p-2 rounded mr-2 disabled:opacity-50"
|
|
47
51
|
title="Save"
|
example/src/init.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { drizzle } from
|
|
1
|
+
import { drizzle } from "drizzle-orm/neon-serverless";
|
|
2
|
-
import { Pool } from
|
|
2
|
+
import { Pool } from "@neondatabase/serverless";
|
|
3
|
-
import { highlight } from
|
|
3
|
+
import { highlight } from "sql-highlight";
|
|
4
4
|
|
|
5
5
|
const init = async () => {
|
|
6
|
+
const pool = new Pool({
|
|
6
|
-
|
|
7
|
+
connectionString: process.env.EDGE_PG_CONN_URL,
|
|
8
|
+
});
|
|
7
9
|
const db = drizzle(pool, {
|
|
8
10
|
logger: {
|
|
9
11
|
logQuery: (query, params) => {
|
|
10
12
|
const sqlString = params.reduce((acc, v, i) => acc.replaceAll("$" + (i + 1), v), query);
|
|
11
13
|
console.log(highlight(sqlString));
|
|
12
|
-
}
|
|
14
|
+
},
|
|
13
|
-
}
|
|
15
|
+
},
|
|
14
16
|
});
|
|
15
17
|
globalThis.db = db;
|
|
16
|
-
}
|
|
18
|
+
};
|
|
17
19
|
|
|
18
|
-
export default init;
|
|
20
|
+
export default init;
|
example/src/pages/_404/page.css
CHANGED
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
justify-content: center;
|
|
6
6
|
color: #000;
|
|
7
7
|
background: #fff;
|
|
8
|
-
font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir,
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir,
|
|
9
|
+
"Helvetica Neue", "Lucida Grande", sans-serif;
|
|
9
10
|
text-align: center;
|
|
10
11
|
|
|
11
12
|
& h1 {
|
|
12
13
|
display: inline-block;
|
|
13
|
-
border-right: 1px solid rgba(0, 0, 0, .3);
|
|
14
|
+
border-right: 1px solid rgba(0, 0, 0, 0.3);
|
|
14
15
|
margin: 0;
|
|
15
16
|
margin-right: 20px;
|
|
16
17
|
padding: 10px 23px 10px 0;
|
|
@@ -34,4 +35,4 @@
|
|
|
34
35
|
margin: 0;
|
|
35
36
|
padding: 0;
|
|
36
37
|
}
|
|
37
|
-
}
|
|
38
|
+
}
|
example/src/pages/_404/page.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
|
-
import { Helmet } from
|
|
2
|
+
import { Helmet } from "react-helmet-async";
|
|
3
3
|
import "./page.css";
|
|
4
4
|
|
|
5
5
|
export default function Page() {
|
|
@@ -13,5 +13,5 @@ export default function Page() {
|
|
|
13
13
|
<h2>This page could not be found</h2>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
|
-
)
|
|
16
|
+
);
|
|
17
|
-
}
|
|
17
|
+
}
|
example/src/pages/_500/page.css
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
justify-content: center;
|
|
6
6
|
color: #000;
|
|
7
7
|
background: #fff;
|
|
8
|
-
font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir,
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir,
|
|
9
|
+
"Helvetica Neue", "Lucida Grande", sans-serif;
|
|
9
10
|
height: 100vh;
|
|
10
11
|
text-align: center;
|
|
11
12
|
|
|
12
13
|
& h1 {
|
|
13
14
|
display: inline-block;
|
|
14
|
-
border-right: 1px solid rgba(0, 0, 0, .3);
|
|
15
|
+
border-right: 1px solid rgba(0, 0, 0, 0.3);
|
|
15
16
|
margin: 0;
|
|
16
17
|
margin-right: 20px;
|
|
17
18
|
padding: 10px 23px 10px 0;
|
|
@@ -35,4 +36,4 @@
|
|
|
35
36
|
margin: 0;
|
|
36
37
|
padding: 0;
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
+
}
|
example/src/pages/_500/page.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
|
-
import { Helmet } from
|
|
2
|
+
import { Helmet } from "react-helmet-async";
|
|
3
3
|
import "./page.css";
|
|
4
4
|
|
|
5
5
|
export default function Page() {
|
|
@@ -13,5 +13,5 @@ export default function Page() {
|
|
|
13
13
|
<h2>Internal Server Error</h2>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
|
-
)
|
|
16
|
+
);
|
|
17
|
-
}
|
|
17
|
+
}
|
example/src/pages/about/page.css
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
& footer {
|
|
7
7
|
margin-top: 100px;
|
|
8
8
|
}
|
|
9
|
-
}
|
|
9
|
+
}
|
example/src/pages/about/page.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { Link, useRouter } from "edge-city";
|
|
3
|
-
import { Helmet } from
|
|
3
|
+
import { Helmet } from "react-helmet-async";
|
|
4
4
|
import "./page.css";
|
|
5
5
|
|
|
6
6
|
export default function Page() {
|
|
@@ -13,14 +13,12 @@ export default function Page() {
|
|
|
13
13
|
</Helmet>
|
|
14
14
|
<div>
|
|
15
15
|
<h1>About Page</h1>
|
|
16
|
-
<p>
|
|
17
|
-
|
|
16
|
+
<p>Path: {router.pathname}</p>
|
|
18
|
-
</p>
|
|
19
17
|
<p>Showcase of using edge-city meta-framework.</p>
|
|
20
18
|
</div>
|
|
21
19
|
<footer>
|
|
22
20
|
<Link href="/">Back</Link>
|
|
23
21
|
</footer>
|
|
24
22
|
</div>
|
|
25
|
-
)
|
|
23
|
+
);
|
|
26
24
|
}
|
example/src/pages/app.css
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
box-sizing: border-box; /* 1 */
|
|
10
10
|
border-width: 0; /* 2 */
|
|
11
11
|
border-style: solid; /* 2 */
|
|
12
|
-
border-color: theme(
|
|
12
|
+
border-color: theme("borderColor.DEFAULT", currentColor); /* 2 */
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
::before,
|
|
16
16
|
::after {
|
|
17
|
-
--tw-content:
|
|
17
|
+
--tw-content: "";
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/*
|
|
@@ -31,9 +31,25 @@ html {
|
|
|
31
31
|
-webkit-text-size-adjust: 100%; /* 2 */
|
|
32
32
|
-moz-tab-size: 4; /* 3 */
|
|
33
33
|
tab-size: 4; /* 3 */
|
|
34
|
-
font-family: theme(
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
font-family: theme(
|
|
35
|
+
"fontFamily.sans",
|
|
36
|
+
ui-sans-serif,
|
|
37
|
+
system-ui,
|
|
38
|
+
-apple-system,
|
|
39
|
+
BlinkMacSystemFont,
|
|
40
|
+
"Segoe UI",
|
|
41
|
+
Roboto,
|
|
42
|
+
"Helvetica Neue",
|
|
43
|
+
Arial,
|
|
44
|
+
"Noto Sans",
|
|
45
|
+
sans-serif,
|
|
46
|
+
"Apple Color Emoji",
|
|
47
|
+
"Segoe UI Emoji",
|
|
48
|
+
"Segoe UI Symbol",
|
|
49
|
+
"Noto Color Emoji"
|
|
50
|
+
); /* 4 */
|
|
51
|
+
font-feature-settings: theme("fontFamily.sans[1].fontFeatureSettings", normal); /* 5 */
|
|
52
|
+
font-variation-settings: theme("fontFamily.sans[1].fontVariationSettings", normal); /* 6 */
|
|
37
53
|
}
|
|
38
54
|
|
|
39
55
|
/*
|
|
@@ -107,7 +123,17 @@ code,
|
|
|
107
123
|
kbd,
|
|
108
124
|
samp,
|
|
109
125
|
pre {
|
|
126
|
+
font-family: theme(
|
|
127
|
+
"fontFamily.mono",
|
|
128
|
+
ui-monospace,
|
|
129
|
+
SFMono-Regular,
|
|
130
|
+
Menlo,
|
|
131
|
+
Monaco,
|
|
132
|
+
Consolas,
|
|
110
|
-
|
|
133
|
+
"Liberation Mono",
|
|
134
|
+
"Courier New",
|
|
135
|
+
monospace
|
|
136
|
+
); /* 1 */
|
|
111
137
|
font-size: 1em; /* 2 */
|
|
112
138
|
}
|
|
113
139
|
|
|
@@ -186,9 +212,9 @@ select {
|
|
|
186
212
|
*/
|
|
187
213
|
|
|
188
214
|
button,
|
|
189
|
-
[type=
|
|
215
|
+
[type="button"],
|
|
190
|
-
[type=
|
|
216
|
+
[type="reset"],
|
|
191
|
-
[type=
|
|
217
|
+
[type="submit"] {
|
|
192
218
|
-webkit-appearance: button; /* 1 */
|
|
193
219
|
background-color: transparent; /* 2 */
|
|
194
220
|
background-image: none; /* 2 */
|
|
@@ -232,7 +258,7 @@ Correct the cursor style of increment and decrement buttons in Safari.
|
|
|
232
258
|
2. Correct the outline style in Safari.
|
|
233
259
|
*/
|
|
234
260
|
|
|
235
|
-
[type=
|
|
261
|
+
[type="search"] {
|
|
236
262
|
-webkit-appearance: textfield; /* 1 */
|
|
237
263
|
outline-offset: -2px; /* 2 */
|
|
238
264
|
}
|
|
@@ -316,7 +342,7 @@ textarea {
|
|
|
316
342
|
input::placeholder,
|
|
317
343
|
textarea::placeholder {
|
|
318
344
|
opacity: 1; /* 1 */
|
|
319
|
-
color: theme(
|
|
345
|
+
color: theme("colors.gray.400", #9ca3af); /* 2 */
|
|
320
346
|
}
|
|
321
347
|
|
|
322
348
|
/*
|
|
@@ -372,7 +398,9 @@ html {
|
|
|
372
398
|
line-height: 1.5;
|
|
373
399
|
font-size: 16px;
|
|
374
400
|
-webkit-text-size-adjust: 100%;
|
|
375
|
-
font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,
|
|
401
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
402
|
+
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
403
|
+
"Segoe UI Symbol", "Noto Color Emoji";
|
|
376
404
|
}
|
|
377
405
|
|
|
378
406
|
body {
|
|
@@ -384,4 +412,4 @@ h1 {
|
|
|
384
412
|
font-size: 1.25rem;
|
|
385
413
|
line-height: 1.75rem;
|
|
386
414
|
font-weight: 700;
|
|
387
|
-
}
|
|
415
|
+
}
|
example/src/pages/app.jsx
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { SSRProvider } from
|
|
1
|
+
import { SSRProvider } from "react-aria";
|
|
2
|
-
import Layout from
|
|
2
|
+
import Layout from "@/components/Layout/Layout";
|
|
3
3
|
import "./app.css";
|
|
4
4
|
|
|
5
5
|
export default function App({ children }) {
|
|
6
6
|
return (
|
|
7
7
|
<SSRProvider>
|
|
8
|
-
<Layout>
|
|
9
|
-
{children}
|
|
10
|
-
</Layout>
|
|
8
|
+
<Layout>{children}</Layout>
|
|
11
9
|
</SSRProvider>
|
|
12
|
-
)
|
|
10
|
+
);
|
|
13
|
-
}
|
|
11
|
+
}
|
example/src/pages/page.css
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
font-size: 30px;
|
|
6
6
|
font-weight: 600;
|
|
7
7
|
}
|
|
8
|
-
}
|
|
8
|
+
}
|
example/src/pages/page.jsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { useRouter } from "edge-city";
|
|
3
3
|
import Counter from "@/components/Counter/Counter";
|
|
4
|
-
import { Helmet } from
|
|
4
|
+
import { Helmet } from "react-helmet-async";
|
|
5
5
|
import "./page.css";
|
|
6
6
|
|
|
7
7
|
export default function Page() {
|
|
@@ -13,11 +13,9 @@ export default function Page() {
|
|
|
13
13
|
</Helmet>
|
|
14
14
|
<div className="home-page">
|
|
15
15
|
<h1>Home Page</h1>
|
|
16
|
-
<p>
|
|
17
|
-
|
|
16
|
+
<p>Path: {router.pathname}</p>
|
|
18
|
-
</p>
|
|
19
17
|
<Counter />
|
|
20
18
|
</div>
|
|
21
19
|
</div>
|
|
22
|
-
)
|
|
23
|
-
|
|
20
|
+
);
|
|
21
|
+
}
|
example/src/pages/page.spec.js
CHANGED
|
@@ -16,4 +16,4 @@
|
|
|
16
16
|
// test('has counter', async ({ page }) => {
|
|
17
17
|
// const counter = page.getByText("Counter");
|
|
18
18
|
// expect(counter.innerText).toEqual("123");
|
|
19
|
-
// });
|
|
19
|
+
// });
|
example/src/pages/todos/page.css
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
margin-right: 1rem;
|
|
72
72
|
border-width: 1px;
|
|
73
73
|
width: 100%;
|
|
74
|
-
padding-left: 0.75rem;
|
|
74
|
+
padding-left: 0.75rem;
|
|
75
75
|
padding-right: 0.75rem;
|
|
76
76
|
padding-bottom: 0.5rem;
|
|
77
77
|
padding-top: 0.5rem;
|
|
@@ -92,11 +92,11 @@
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
[slot=description] {
|
|
95
|
+
[slot="description"] {
|
|
96
96
|
font-size: 12px;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
[slot=errorMessage] {
|
|
99
|
+
[slot="errorMessage"] {
|
|
100
100
|
font-size: 12px;
|
|
101
101
|
color: var(--invalid-color);
|
|
102
102
|
}
|
|
@@ -112,4 +112,4 @@
|
|
|
112
112
|
--focus-ring-color: Highlight;
|
|
113
113
|
--invalid-color: LinkText;
|
|
114
114
|
}
|
|
115
|
-
}
|
|
115
|
+
}
|
example/src/pages/todos/page.jsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
|
-
import { Helmet } from
|
|
2
|
+
import { Helmet } from "react-helmet-async";
|
|
3
3
|
import { useQuery, useMutation } from "edge-city";
|
|
4
|
-
import { useForm } from
|
|
4
|
+
import { useForm } from "react-hook-form";
|
|
5
|
-
import { Button, TextField, Input } from
|
|
5
|
+
import { Button, TextField, Input } from "react-aria-components";
|
|
6
6
|
import Todo from "@/components/Todo/Todo";
|
|
7
7
|
import { getTodos, createTodo, updateTodo, deleteTodo } from "@/services/todos.service";
|
|
8
8
|
import "./page.css";
|
|
@@ -13,7 +13,7 @@ export default function Page() {
|
|
|
13
13
|
await createTodo({
|
|
14
14
|
text,
|
|
15
15
|
completed: false,
|
|
16
|
-
})
|
|
16
|
+
});
|
|
17
17
|
await refetch();
|
|
18
18
|
});
|
|
19
19
|
const updateMutation = useMutation(async ({ text, completed }) => {
|
|
@@ -23,8 +23,12 @@ export default function Page() {
|
|
|
23
23
|
const deleteMutation = useMutation(async (id) => {
|
|
24
24
|
await deleteTodo(id);
|
|
25
25
|
await refetch();
|
|
26
|
-
})
|
|
26
|
+
});
|
|
27
|
+
const {
|
|
28
|
+
register,
|
|
29
|
+
handleSubmit,
|
|
27
|
-
|
|
30
|
+
formState: { errors },
|
|
31
|
+
} = useForm();
|
|
28
32
|
return (
|
|
29
33
|
<div className="todos-page">
|
|
30
34
|
<h1 className="title">Todo List</h1>
|
|
@@ -35,10 +39,12 @@ export default function Page() {
|
|
|
35
39
|
<p className="subtitle">Share this page to collaborate with others.</p>
|
|
36
40
|
<form onSubmit={handleSubmit(mutate)}>
|
|
37
41
|
<TextField isRequired isReadOnly={isMutating}>
|
|
38
|
-
<Input {...register(
|
|
42
|
+
<Input {...register("text")} placeholder="Add a todo item" />
|
|
39
43
|
{err?.text && <p>{err.text._errors[0]}</p>}
|
|
40
44
|
</TextField>
|
|
41
|
-
<Button className="add-button" type="submit" isDisabled={isMutating}>
|
|
45
|
+
<Button className="add-button" type="submit" isDisabled={isMutating}>
|
|
46
|
+
Add
|
|
47
|
+
</Button>
|
|
42
48
|
</form>
|
|
43
49
|
<ul>
|
|
44
50
|
{data.map((item) => (
|
|
@@ -52,5 +58,5 @@ export default function Page() {
|
|
|
52
58
|
</ul>
|
|
53
59
|
</div>
|
|
54
60
|
</div>
|
|
55
|
-
)
|
|
61
|
+
);
|
|
56
|
-
}
|
|
62
|
+
}
|
example/src/services/todos.service.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { eq, asc } from
|
|
1
|
+
import { eq, asc } from "drizzle-orm";
|
|
2
|
-
import { boolean, date, pgTable, serial, text } from
|
|
2
|
+
import { boolean, date, pgTable, serial, text } from "drizzle-orm/pg-core";
|
|
3
|
-
import { z } from
|
|
3
|
+
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
-
export const todos = pgTable(
|
|
5
|
+
export const todos = pgTable("todos", {
|
|
6
|
-
id: serial(
|
|
6
|
+
id: serial("id").primaryKey(),
|
|
7
|
-
text: text(
|
|
7
|
+
text: text("text").notNull(),
|
|
8
|
-
completed: boolean(
|
|
8
|
+
completed: boolean("completed").notNull(),
|
|
9
|
-
createdAt: date(
|
|
9
|
+
createdAt: date("createdAt").notNull(),
|
|
10
|
-
updatedAt: date(
|
|
10
|
+
updatedAt: date("updatedAt"),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
export const createSchema = z.object({
|
|
@@ -22,27 +22,27 @@ const updateSchema = z.object({
|
|
|
22
22
|
|
|
23
23
|
export const getTodos = async () => {
|
|
24
24
|
return await db.select().from(todos).orderBy(asc(todos.id));
|
|
25
|
-
}
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
/** @param {z.infer<typeof createSchema>} params */
|
|
28
28
|
export const createTodo = async (params) => {
|
|
29
29
|
const item = createSchema.parse(params);
|
|
30
30
|
item.createdAt = new Date();
|
|
31
31
|
return await db.insert(todos).values(item).returning();
|
|
32
|
-
}
|
|
32
|
+
};
|
|
33
33
|
|
|
34
34
|
export const getTodo = async (id) => {
|
|
35
35
|
const results = await db.select().from(todos).where(eq(todos.id, id));
|
|
36
|
-
return results[0]
|
|
36
|
+
return results[0];
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
|
|
39
39
|
/** @param {z.infer<typeof updateSchema>} params */
|
|
40
40
|
export const updateTodo = async (params) => {
|
|
41
41
|
const item = updateSchema.parse(params);
|
|
42
42
|
item.updatedAt = new Date();
|
|
43
43
|
return await db.update(todos).set(item).where(eq(todos.id, item.id)).returning();
|
|
44
|
-
}
|
|
44
|
+
};
|
|
45
45
|
|
|
46
46
|
export const deleteTodo = async (id) => {
|
|
47
47
|
return await db.delete(todos).where(eq(todos.id, id)).returning();
|
|
48
|
-
}
|
|
48
|
+
};
|
example/src/services/todos.service.test.js
CHANGED
|
@@ -3,38 +3,34 @@ import { createSchema } from "./todos.service";
|
|
|
3
3
|
test("validate createSchema", () => {
|
|
4
4
|
expect(createSchema.safeParse({}).error.issues).toEqual([
|
|
5
5
|
{
|
|
6
|
-
|
|
6
|
+
code: "invalid_type",
|
|
7
|
-
|
|
7
|
+
expected: "string",
|
|
8
|
-
|
|
8
|
+
message: "Required",
|
|
9
|
-
"path": [
|
|
10
|
-
|
|
9
|
+
path: ["text"],
|
|
11
|
-
],
|
|
12
|
-
|
|
10
|
+
received: "undefined",
|
|
13
11
|
},
|
|
14
12
|
{
|
|
15
|
-
|
|
13
|
+
code: "invalid_type",
|
|
16
|
-
|
|
14
|
+
expected: "boolean",
|
|
17
|
-
|
|
15
|
+
message: "Required",
|
|
18
|
-
"path": [
|
|
19
|
-
|
|
16
|
+
path: ["completed"],
|
|
20
|
-
],
|
|
21
|
-
|
|
17
|
+
received: "undefined",
|
|
22
|
-
}
|
|
18
|
+
},
|
|
23
|
-
])
|
|
19
|
+
]);
|
|
20
|
+
expect(
|
|
24
|
-
|
|
21
|
+
createSchema.safeParse({
|
|
25
|
-
|
|
22
|
+
text: "",
|
|
26
|
-
|
|
23
|
+
completed: true,
|
|
27
|
-
|
|
24
|
+
}).error.issues,
|
|
25
|
+
).toEqual([
|
|
28
26
|
{
|
|
29
|
-
|
|
27
|
+
code: "too_small",
|
|
30
|
-
|
|
28
|
+
exact: false,
|
|
31
|
-
|
|
29
|
+
inclusive: true,
|
|
32
|
-
|
|
30
|
+
message: "please enter some text",
|
|
33
|
-
|
|
31
|
+
minimum: 1,
|
|
34
|
-
"path": [
|
|
35
|
-
|
|
32
|
+
path: ["text"],
|
|
36
|
-
],
|
|
37
|
-
|
|
33
|
+
type: "string",
|
|
38
34
|
},
|
|
39
|
-
])
|
|
35
|
+
]);
|
|
40
|
-
})
|
|
36
|
+
});
|
pnpm-lock.yaml
CHANGED
|
@@ -135,6 +135,9 @@ importers:
|
|
|
135
135
|
postgres:
|
|
136
136
|
specifier: 3.3.4
|
|
137
137
|
version: 3.3.4
|
|
138
|
+
prettier:
|
|
139
|
+
specifier: ^2.8.8
|
|
140
|
+
version: 2.8.8
|
|
138
141
|
wrangler:
|
|
139
142
|
specifier: 3.0.1
|
|
140
143
|
version: 3.0.1
|
|
@@ -7420,6 +7423,12 @@ packages:
|
|
|
7420
7423
|
engines: {node: '>= 0.8.0'}
|
|
7421
7424
|
dev: true
|
|
7422
7425
|
|
|
7426
|
+
/prettier@2.8.8:
|
|
7427
|
+
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
|
|
7428
|
+
engines: {node: '>=10.13.0'}
|
|
7429
|
+
hasBin: true
|
|
7430
|
+
dev: true
|
|
7431
|
+
|
|
7423
7432
|
/pretty-format@29.5.0:
|
|
7424
7433
|
resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==}
|
|
7425
7434
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|