Compare commits
40 Commits
jazz-react
...
marina-onb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dc90a3554 | ||
|
|
51b1818de6 | ||
|
|
222a2ce2e6 | ||
|
|
34a50a9173 | ||
|
|
72c6198ef7 | ||
|
|
e4df1048c8 | ||
|
|
e8abd06406 | ||
|
|
968c2bd699 | ||
|
|
12b6a3b291 | ||
|
|
c9f89e9c32 | ||
|
|
c0395dd0a3 | ||
|
|
51d7ca09d9 | ||
|
|
cf0a38d6bf | ||
|
|
dd9b13fbaa | ||
|
|
6a982a29cb | ||
|
|
ebc1b03158 | ||
|
|
c6931b82a0 | ||
|
|
279e2202ba | ||
|
|
ec2324519e | ||
|
|
5932f50c68 | ||
|
|
00906e5d08 | ||
|
|
7bc5fca440 | ||
|
|
e30eb224ae | ||
|
|
983ea7cf03 | ||
|
|
ad2d453e33 | ||
|
|
15d711f6de | ||
|
|
7441a7d3d8 | ||
|
|
0d756209e9 | ||
|
|
b20c2ca173 | ||
|
|
987a186db3 | ||
|
|
f4e0b59fa1 | ||
|
|
0d2112d8d0 | ||
|
|
6fdab780a9 | ||
|
|
159e2eb7f6 | ||
|
|
9daa50dd7d | ||
|
|
9c2aadb7d5 | ||
|
|
7d0d81b16e | ||
|
|
760a1c10c5 | ||
|
|
3ef3ff3db9 | ||
|
|
549ec2047f |
5
.changeset/afraid-ladybugs-jam.md
Normal file
5
.changeset/afraid-ladybugs-jam.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"jazz-react": patch
|
||||
---
|
||||
|
||||
Move auto login check to useEffect
|
||||
8
.changeset/real-pianos-press.md
Normal file
8
.changeset/real-pianos-press.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"jazz-react-native-media-images": patch
|
||||
"cojson-storage-indexeddb": patch
|
||||
"jazz-react-native": patch
|
||||
"jazz-react": patch
|
||||
---
|
||||
|
||||
Remove typescript from the direct dependencies
|
||||
5
.changeset/short-mails-shave.md
Normal file
5
.changeset/short-mails-shave.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"jazz-react": patch
|
||||
---
|
||||
|
||||
Add user prop to demo auth to skip login on demos
|
||||
3
.github/workflows/build-examples.yaml
vendored
3
.github/workflows/build-examples.yaml
vendored
@@ -17,6 +17,7 @@ jobs:
|
||||
"password-manager",
|
||||
"pets",
|
||||
"todo",
|
||||
"onboarding",
|
||||
]
|
||||
|
||||
steps:
|
||||
@@ -55,4 +56,4 @@ jobs:
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm turbo build;
|
||||
working-directory: ./examples/${{ matrix.example }}
|
||||
working-directory: ./examples/${{ matrix.example }}
|
||||
|
||||
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
project: ["e2e/BinaryCoStream", "e2e/CoValues", "examples/chat", "examples/music-player", "examples/pets"]
|
||||
project: ["e2e/BinaryCoStream", "e2e/CoValues", "examples/chat", "examples/music-player", "examples/pets", "examples/onboarding"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @jazz-e2e/binarycostream
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/binarycostream",
|
||||
"private": true,
|
||||
"version": "0.0.96",
|
||||
"version": "0.0.97",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,11 +13,11 @@
|
||||
"test:ui": "playwright test --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @jazz-e2e/covalues
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.95
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/covalues",
|
||||
"private": true,
|
||||
"version": "0.0.95",
|
||||
"version": "0.0.96",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-book-shelf
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
- jazz-browser-media-images@0.8.19
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-example-book-shelf",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.12",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -11,9 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-browser-media-images": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-browser-media-images": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"next": "14.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-react@0.8.19
|
||||
- jazz-react-auth-clerk@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.95
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat-clerk",
|
||||
"private": true,
|
||||
"version": "0.0.95",
|
||||
"version": "0.0.96",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -17,11 +17,11 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-auth-clerk@0.8.19
|
||||
- jazz-react-native@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
- jazz-react-native-media-images@0.8.15
|
||||
|
||||
## 1.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 1.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.14",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
- jazz-vue@0.8.9
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.98",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -18,10 +18,10 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- cojson-transport-ws@0.8.19
|
||||
|
||||
## 0.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector",
|
||||
"private": true,
|
||||
"version": "0.0.71",
|
||||
"version": "0.0.72",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -16,8 +16,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson-transport-ws": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.17",
|
||||
"version": "0.0.18",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -18,8 +18,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
26
examples/onboarding/.gitignore
vendored
Normal file
26
examples/onboarding/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
playwright-report
|
||||
4
examples/onboarding/Dockerfile
Normal file
4
examples/onboarding/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM caddy:2.7.3-alpine
|
||||
LABEL org.opencontainers.image.source="https://github.com/gardencmp/jazz"
|
||||
|
||||
COPY ./dist /usr/share/caddy/
|
||||
1
examples/onboarding/README.md
Normal file
1
examples/onboarding/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# vite-ts-react-tailwind
|
||||
12
examples/onboarding/index.html
Normal file
12
examples/onboarding/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Jazz onboarding example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
36
examples/onboarding/package.json
Normal file
36
examples/onboarding/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "jazz-example-onboarding",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"test:ui": "playwright test --ui",
|
||||
"sync": "jazz-run sync"
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-browser-media-images": "workspace:*",
|
||||
"jazz-react": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jazz-run": "workspace:*",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@types/react": "^18.2.19",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^5.3.3",
|
||||
"is-ci": "^3.0.1",
|
||||
"vite": "^5.0.10"
|
||||
}
|
||||
}
|
||||
51
examples/onboarding/playwright.config.ts
Normal file
51
examples/onboarding/playwright.config.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import isCI from "is-ci";
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: isCI,
|
||||
/* Retry on CI only */
|
||||
retries: isCI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: isCI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: "http://localhost:5173/?peer=ws://localhost:1234",
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
permissions: ["clipboard-read", "clipboard-write"],
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm preview --port 5173",
|
||||
url: "http://localhost:5173/",
|
||||
reuseExistingServer: !isCI,
|
||||
},
|
||||
{
|
||||
command: "pnpm sync --in-memory --port 1234",
|
||||
url: "http://localhost:1234/health",
|
||||
reuseExistingServer: !isCI,
|
||||
},
|
||||
],
|
||||
});
|
||||
6
examples/onboarding/postcss.config.js
Normal file
6
examples/onboarding/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
BIN
examples/onboarding/public/jazz-logo-low-res.jpg
Normal file
BIN
examples/onboarding/public/jazz-logo-low-res.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 820 B |
83
examples/onboarding/src/App.tsx
Normal file
83
examples/onboarding/src/App.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Button } from "@/components/Button.tsx";
|
||||
import { useAccount, useCoState } from "@/main.tsx";
|
||||
import { EmployeeList } from "@/pages/EmployeeList.tsx";
|
||||
import { EmployeeOnboading } from "@/pages/EmployeeOnboarding.tsx";
|
||||
import { NewEmployee } from "@/pages/NewEmployee.tsx";
|
||||
import { CoEmployee, EmployeeCoList } from "@/schema.ts";
|
||||
import { ID } from "jazz-tools";
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
RouterProvider,
|
||||
createBrowserRouter,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
|
||||
function ImportEmployee({
|
||||
employeeListCoId,
|
||||
}: { employeeListCoId: ID<EmployeeCoList> }) {
|
||||
const { employeeCoId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
const employee = useCoState(CoEmployee, employeeCoId as ID<CoEmployee>, {});
|
||||
|
||||
useEffect(() => {
|
||||
if (!employee || !employees) return;
|
||||
|
||||
const exists = employees.find((employee) => employeeCoId === employee.id);
|
||||
|
||||
if (!exists) {
|
||||
employees.push(employee);
|
||||
}
|
||||
navigate("/");
|
||||
}, [employee, employees, navigate]);
|
||||
|
||||
return <div>Importing Employee ${employeeCoId} ...</div>;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const { me, logOut } = useAccount();
|
||||
const employeeCoListId = me.profile?._refs.employees.id;
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <EmployeeList employeeListCoId={employeeCoListId} />,
|
||||
},
|
||||
{
|
||||
path: "employee/new",
|
||||
element: <NewEmployee employeeListCoId={employeeCoListId} />,
|
||||
},
|
||||
{
|
||||
path: "/employee/:employeeCoId",
|
||||
element: <EmployeeOnboading />,
|
||||
},
|
||||
{
|
||||
path: "/import/:employeeCoId",
|
||||
element: <ImportEmployee employeeListCoId={employeeCoListId} />,
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="flex flex-wrap space-x-8 max-w-screen-lg m-2">
|
||||
<h1 className="text-3xl font-extrabold">
|
||||
Jazz Onboarding Flow example
|
||||
</h1>
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.location.href = "/";
|
||||
logOut();
|
||||
}}
|
||||
text="Log Out"
|
||||
/>
|
||||
</header>
|
||||
<main className="ml-2">
|
||||
{employeeCoListId && <RouterProvider router={router} />}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
29
examples/onboarding/src/components/Button.tsx
Normal file
29
examples/onboarding/src/components/Button.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
const disabledClasses =
|
||||
"text-white bg-gray-400 dark:bg-gray-500 cursor-not-allowed";
|
||||
const regularClasses =
|
||||
"text-white bg-gradient-to-r from-green-400 via-green-500 to-green-600 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800";
|
||||
|
||||
export function Button({
|
||||
text,
|
||||
onClick,
|
||||
disabled,
|
||||
...props
|
||||
}: {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className={`${
|
||||
disabled ? disabledClasses : regularClasses
|
||||
} text-base font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2`}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
19
examples/onboarding/src/components/ButtonLink.tsx
Normal file
19
examples/onboarding/src/components/ButtonLink.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function ButtonLink({
|
||||
to,
|
||||
children,
|
||||
}: {
|
||||
to: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
|
||||
to={to}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
19
examples/onboarding/src/components/NavLink.tsx
Normal file
19
examples/onboarding/src/components/NavLink.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function NavLink({
|
||||
to,
|
||||
children,
|
||||
}: {
|
||||
to: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
className="font-medium text-blue-600 dark:text-blue-500 hover:underline"
|
||||
to={to}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
24
examples/onboarding/src/components/NavigateBack.tsx
Normal file
24
examples/onboarding/src/components/NavigateBack.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button } from "./Button.tsx";
|
||||
|
||||
export function NavigateBack() {
|
||||
const navigate = useNavigate();
|
||||
const canGoBack = window.history.state.idx !== 0;
|
||||
|
||||
if (!canGoBack) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => navigate(-1)} text="< Back" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavigateButton({ text, to }: { text: string; to: string }) {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => navigate(to)} text={text} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
19
examples/onboarding/src/components/Stack.tsx
Normal file
19
examples/onboarding/src/components/Stack.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
|
||||
export function Stack({
|
||||
children,
|
||||
horizontal,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
horizontal?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`container flex ${
|
||||
horizontal ? "flex-row" : "flex-col"
|
||||
} col ${horizontal ? "space-x-4 flex-wrap" : "space-y-4"} p-4`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
36
examples/onboarding/src/components/TextInput.tsx
Normal file
36
examples/onboarding/src/components/TextInput.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ChangeEvent } from "react";
|
||||
|
||||
export function TextInput({
|
||||
id,
|
||||
value,
|
||||
label,
|
||||
onChange,
|
||||
disabled,
|
||||
}: {
|
||||
id: string;
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="block mb-2 font-medium text-gray-900 dark:text-white"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
type="text"
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
className="disabled:cursor-not-allowed bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="John"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
examples/onboarding/src/index.css
Normal file
3
examples/onboarding/src/index.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
37
examples/onboarding/src/main.tsx
Normal file
37
examples/onboarding/src/main.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import App from "@/App.tsx";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "@/index.css";
|
||||
import { HRAccount } from "@/schema.ts";
|
||||
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
|
||||
|
||||
const Jazz = createJazzReactApp({
|
||||
AccountSchema: HRAccount,
|
||||
});
|
||||
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
|
||||
|
||||
function JazzAndAuth({ children }: { children: React.ReactNode }) {
|
||||
const [auth, authState] = useDemoAuth();
|
||||
return (
|
||||
<>
|
||||
<Jazz.Provider
|
||||
auth={auth}
|
||||
// replace `you@example.com` with your email as a temporary API key
|
||||
peer="wss://cloud.jazz.tools/?key=you@example.com"
|
||||
>
|
||||
{children}
|
||||
</Jazz.Provider>
|
||||
{authState.state !== "signedIn" && (
|
||||
<DemoAuthBasicUI appName="Jazz Onboarding" state={authState} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<JazzAndAuth>
|
||||
<App />
|
||||
</JazzAndAuth>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
50
examples/onboarding/src/pages/EmployeeList.tsx
Normal file
50
examples/onboarding/src/pages/EmployeeList.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { NavLink } from "@/components/NavLink.tsx";
|
||||
import { NavigateButton } from "@/components/NavigateBack.tsx";
|
||||
import { Stack } from "@/components/Stack.tsx";
|
||||
import { useCoState } from "@/main.tsx";
|
||||
import { CoEmployee, EmployeeCoList } from "@/schema.ts";
|
||||
import { ID } from "jazz-tools";
|
||||
|
||||
export function EmployeeList({
|
||||
employeeListCoId,
|
||||
}: {
|
||||
employeeListCoId: ID<EmployeeCoList>;
|
||||
}) {
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
|
||||
if (!employees) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<NavigateButton to="/employee/new" text={"Add New Employee"} />
|
||||
<ul className="max-w-md">
|
||||
{employees.map((employee: CoEmployee) =>
|
||||
employee.deleted ? null : (
|
||||
<li key={employee.id} className="flex flex-row space-x-8 w-full">
|
||||
<span>{employee._owner.myRole()}</span>
|
||||
<span className="w-1/3">
|
||||
<NavLink to={`/employee/${employee.id}`}>
|
||||
{employee.name}
|
||||
</NavLink>
|
||||
</span>
|
||||
{employee.finalStep?.done && <span>✅</span>}
|
||||
{employee._owner.myRole() === "admin" &&
|
||||
!employee.finalStep?.done && (
|
||||
<span
|
||||
onClick={() => {
|
||||
employee.deleted = true;
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
🗑
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
249
examples/onboarding/src/pages/EmployeeOnboarding.tsx
Normal file
249
examples/onboarding/src/pages/EmployeeOnboarding.tsx
Normal file
@@ -0,0 +1,249 @@
|
||||
import { Button } from "@/components/Button.tsx";
|
||||
import { NavigateBack } from "@/components/NavigateBack.tsx";
|
||||
import { Stack } from "@/components/Stack.tsx";
|
||||
import { TextInput } from "@/components/TextInput.tsx";
|
||||
import { useAcceptInvite, useCoState } from "@/main.tsx";
|
||||
import { createImage } from "jazz-browser-media-images";
|
||||
import { ProgressiveImg, createInviteLink } from "jazz-react";
|
||||
import { CoMap, ID } from "jazz-tools";
|
||||
import { ChangeEvent, ReactNode, useCallback } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
CoDocUploadStep,
|
||||
CoEmployee,
|
||||
CoFinalStep,
|
||||
CoInitialStep,
|
||||
} from "../schema.ts";
|
||||
|
||||
const Card = ({
|
||||
children,
|
||||
title,
|
||||
isDone,
|
||||
isActive,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
isDone: boolean;
|
||||
isActive?: boolean;
|
||||
}) => (
|
||||
<div
|
||||
className={`w-full p-4 bg-white border border-gray-200 rounded-lg shadow max-w-md ${
|
||||
isActive ? "border-gray-900 hover:bg-green-50 shadow-xl" : ""
|
||||
}`}
|
||||
>
|
||||
<Stack horizontal={true}>
|
||||
<h5 className="mb-2 text-2xl text-gray-900">{title}</h5>
|
||||
<h6 className="mb-2 text-2xl">
|
||||
{isDone ? "✅" : isActive ? "❓" : "⌛"}
|
||||
</h6>
|
||||
</Stack>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
const InfoCard = ({
|
||||
initialStep,
|
||||
canWrite,
|
||||
}: {
|
||||
initialStep: CoInitialStep;
|
||||
canWrite: boolean;
|
||||
}) => {
|
||||
const isDisabled = !initialStep.isCurrentStep() || !canWrite;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="Personal Info"
|
||||
isDone={initialStep?.done}
|
||||
isActive={initialStep.isCurrentStep()}
|
||||
>
|
||||
<Stack>
|
||||
<TextInput
|
||||
disabled={isDisabled}
|
||||
id="ssn"
|
||||
label="Social Security Number"
|
||||
value={initialStep.ssn || ""}
|
||||
onChange={({ target: { value } }) => (initialStep.ssn = value)}
|
||||
/>
|
||||
<TextInput
|
||||
disabled={isDisabled}
|
||||
id="address"
|
||||
label="Address"
|
||||
value={initialStep.address || ""}
|
||||
onChange={({ target: { value } }) => (initialStep.address = value)}
|
||||
/>
|
||||
{!initialStep.done && (
|
||||
<Button
|
||||
text={"Upload step >"}
|
||||
disabled={!initialStep.ssn || !initialStep.address || isDisabled}
|
||||
onClick={() => (initialStep.done = true)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const UploadCard = ({
|
||||
uploadStep,
|
||||
canWrite,
|
||||
}: {
|
||||
uploadStep: CoDocUploadStep;
|
||||
canWrite: boolean;
|
||||
}) => {
|
||||
const isDisabled = !uploadStep.isCurrentStep() || !canWrite;
|
||||
|
||||
const onImageSelected = useCallback(
|
||||
async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!event.target.files) return;
|
||||
|
||||
const image = await createImage(event.target.files[0], {
|
||||
owner: uploadStep._owner,
|
||||
});
|
||||
|
||||
uploadStep.photo = image;
|
||||
},
|
||||
[uploadStep],
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="Uploads"
|
||||
isDone={uploadStep?.done}
|
||||
isActive={uploadStep.isCurrentStep()}
|
||||
>
|
||||
<Stack>
|
||||
{uploadStep.photo && (
|
||||
<ProgressiveImg image={uploadStep.photo}>
|
||||
{({ src }) => (
|
||||
<img
|
||||
className="max-h-full max-w-full rounded-l-sm rounded-r-md shadow-lg p-2"
|
||||
src={src}
|
||||
/>
|
||||
)}
|
||||
</ProgressiveImg>
|
||||
)}
|
||||
|
||||
{!uploadStep.done && (
|
||||
<>
|
||||
<input
|
||||
type="file"
|
||||
disabled={isDisabled}
|
||||
onChange={onImageSelected}
|
||||
data-testid="file-upload"
|
||||
/>
|
||||
<Button
|
||||
text={"Confirmation step >"}
|
||||
disabled={isDisabled || !uploadStep.photo}
|
||||
onClick={() => (uploadStep.done = true)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfirmationCard = ({
|
||||
finalStep,
|
||||
editable,
|
||||
}: {
|
||||
finalStep: CoFinalStep;
|
||||
editable: boolean;
|
||||
}) => {
|
||||
const isDisabled = !finalStep.isCurrentStep() || !editable;
|
||||
return (
|
||||
<Card
|
||||
title="Confirmation by admin"
|
||||
isDone={finalStep?.done}
|
||||
isActive={finalStep.isCurrentStep()}
|
||||
>
|
||||
<Stack>
|
||||
{!finalStep.done && (
|
||||
<Button
|
||||
text="Confirmation by admin"
|
||||
disabled={isDisabled}
|
||||
onClick={() => (finalStep.done = true)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export function EmployeeOnboading() {
|
||||
const { employeeCoId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const employee = useCoState(CoEmployee, employeeCoId as ID<CoEmployee>, {});
|
||||
|
||||
useAcceptInvite({
|
||||
invitedObjectSchema: CoEmployee,
|
||||
onAccept: (employeeCoId) => {
|
||||
navigate(`/import/${employeeCoId}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleInviteLinkCreation = useCallback(
|
||||
(role: "reader" | "writer") => {
|
||||
if (!employee) return;
|
||||
|
||||
const link = createInviteLink(employee, role);
|
||||
navigator.clipboard.writeText(link);
|
||||
alert("Invite link copied to clipboard!");
|
||||
},
|
||||
[employee],
|
||||
);
|
||||
|
||||
const isMeWriter = (step: CoMap): boolean => {
|
||||
return ["writer", "admin"].includes(step._owner.myRole() || "");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Stack horizontal={true}>
|
||||
<NavigateBack />
|
||||
{employee?._owner.myRole() === "admin" && (
|
||||
<Button
|
||||
text={"Invite a co-worker"}
|
||||
onClick={() => handleInviteLinkCreation("writer")}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
<h2 className="mb-2 text-2xl text-gray-900 font-semibold">
|
||||
{employee ? employee.name : "Loading..."}
|
||||
</h2>
|
||||
</Stack>
|
||||
|
||||
{employee && (
|
||||
<Stack>
|
||||
{employee.initialStep ? (
|
||||
<InfoCard
|
||||
initialStep={employee.initialStep}
|
||||
canWrite={isMeWriter(employee.initialStep)}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
{employee.docUploadStep ? (
|
||||
<UploadCard
|
||||
uploadStep={employee.docUploadStep}
|
||||
canWrite={isMeWriter(employee.docUploadStep)}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
{employee.finalStep ? (
|
||||
<ConfirmationCard
|
||||
finalStep={employee.finalStep}
|
||||
editable={isMeWriter(employee.finalStep)}
|
||||
/>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
89
examples/onboarding/src/pages/NewEmployee.tsx
Normal file
89
examples/onboarding/src/pages/NewEmployee.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Button } from "@/components/Button.tsx";
|
||||
import { NavigateBack } from "@/components/NavigateBack.tsx";
|
||||
import { Stack } from "@/components/Stack.tsx";
|
||||
import { TextInput } from "@/components/TextInput.tsx";
|
||||
import { useAccount, useCoState } from "@/main.tsx";
|
||||
import { Group, ID } from "jazz-tools";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
CoDocUploadStep,
|
||||
CoEmployee,
|
||||
CoFinalStep,
|
||||
CoInitialStep,
|
||||
EmployeeCoList,
|
||||
} from "../schema.ts";
|
||||
|
||||
export function NewEmployee({
|
||||
employeeListCoId,
|
||||
}: {
|
||||
employeeListCoId: ID<EmployeeCoList>;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { me } = useAccount();
|
||||
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
|
||||
const [employeeName, setEmployeeName] = useState<string>("");
|
||||
|
||||
const createEmployee = useCallback(() => {
|
||||
if (!employees) return;
|
||||
|
||||
const writerGroup = Group.create({ owner: me });
|
||||
const readerGroup = Group.create({ owner: me });
|
||||
readerGroup.addMember("everyone", "reader");
|
||||
|
||||
const initialStep = CoInitialStep.create(
|
||||
{ done: false, type: "initial" },
|
||||
{ owner: writerGroup },
|
||||
);
|
||||
|
||||
const docUploadStep = CoDocUploadStep.create(
|
||||
{ done: false, prevStep: initialStep, type: "upload" },
|
||||
{ owner: writerGroup },
|
||||
);
|
||||
|
||||
const finalStep = CoFinalStep.create(
|
||||
{ done: false, prevStep: docUploadStep, type: "final" },
|
||||
{ owner: readerGroup },
|
||||
);
|
||||
|
||||
const employee = CoEmployee.create(
|
||||
{
|
||||
name: employeeName,
|
||||
initialStep,
|
||||
docUploadStep,
|
||||
finalStep,
|
||||
},
|
||||
{ owner: writerGroup },
|
||||
);
|
||||
|
||||
employees.push(employee);
|
||||
setEmployeeName("");
|
||||
}, [employeeName, employees]);
|
||||
|
||||
return (
|
||||
<div className="w-96">
|
||||
<Stack>
|
||||
<NavigateBack />
|
||||
<form>
|
||||
<TextInput
|
||||
label="Employee name"
|
||||
id="employee-name"
|
||||
value={employeeName}
|
||||
onChange={({ target: { value } }) => setEmployeeName(value)}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<Button
|
||||
disabled={!employeeName}
|
||||
onClick={() => {
|
||||
createEmployee();
|
||||
navigate("/");
|
||||
}}
|
||||
text="Create Employee"
|
||||
/>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
78
examples/onboarding/src/schema.ts
Normal file
78
examples/onboarding/src/schema.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
Account,
|
||||
CoList,
|
||||
CoMap,
|
||||
ImageDefinition,
|
||||
Profile,
|
||||
co,
|
||||
} from "jazz-tools";
|
||||
|
||||
type Steps = "initial" | "upload" | "final";
|
||||
|
||||
interface Step {
|
||||
type: Steps;
|
||||
prevStep: ReturnType<typeof co.ref> | undefined;
|
||||
done: boolean;
|
||||
|
||||
isCurrentStep(): boolean;
|
||||
}
|
||||
|
||||
export class CoInitialStep extends CoMap implements Step {
|
||||
type = co.literal("initial");
|
||||
ssn? = co.string;
|
||||
address? = co.string;
|
||||
done = co.boolean;
|
||||
prevStep = co.null;
|
||||
isCurrentStep() {
|
||||
return !this.done;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoDocUploadStep extends CoMap implements Step {
|
||||
type = co.literal("upload");
|
||||
prevStep = co.ref(CoInitialStep);
|
||||
photo = co.ref(ImageDefinition, { optional: true });
|
||||
done = co.boolean;
|
||||
|
||||
isCurrentStep() {
|
||||
return !!(this.prevStep?.done && !this.done);
|
||||
}
|
||||
}
|
||||
|
||||
export class CoFinalStep extends CoMap implements Step {
|
||||
type = co.literal("final");
|
||||
prevStep = co.ref(CoDocUploadStep);
|
||||
done = co.boolean;
|
||||
|
||||
isCurrentStep() {
|
||||
return !!(this.prevStep?.done && !this.done);
|
||||
}
|
||||
}
|
||||
|
||||
export class CoEmployee extends CoMap {
|
||||
name = co.string;
|
||||
deleted? = co.boolean;
|
||||
initialStep = co.ref(CoInitialStep);
|
||||
docUploadStep = co.ref(CoDocUploadStep);
|
||||
finalStep = co.ref(CoFinalStep);
|
||||
}
|
||||
|
||||
export class EmployeeCoList extends CoList.Of(co.ref(CoEmployee)) {}
|
||||
|
||||
export class HRProfile extends Profile {
|
||||
employees = co.ref(EmployeeCoList);
|
||||
}
|
||||
|
||||
export class HRAccount extends Account {
|
||||
profile = co.ref(HRProfile)!;
|
||||
|
||||
migrate(this: HRAccount, creationProps?: { name: string }) {
|
||||
super.migrate(creationProps);
|
||||
|
||||
if (!this.profile._refs.employees) {
|
||||
this.profile.employees = EmployeeCoList.create([], {
|
||||
owner: this.profile._owner,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
1
examples/onboarding/src/vite-env.d.ts
vendored
Normal file
1
examples/onboarding/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
8
examples/onboarding/tailwind.config.js
Normal file
8
examples/onboarding/tailwind.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
88
examples/onboarding/tests/onboardingFlow.spec.ts
Normal file
88
examples/onboarding/tests/onboardingFlow.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Page, expect, test } from "@playwright/test";
|
||||
import { EmployeeOnboardingPage } from "./pages/EmployeeOnboardingPage";
|
||||
import { HomePage } from "./pages/HomePage";
|
||||
import { LoginPage } from "./pages/LoginPage";
|
||||
|
||||
async function scrollToBottom(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
}
|
||||
|
||||
const login = async ({
|
||||
page,
|
||||
userName,
|
||||
loginAs = false,
|
||||
}: {
|
||||
page: Page;
|
||||
userName: string;
|
||||
loginAs?: boolean;
|
||||
}) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
await loginPage.goto("/");
|
||||
if (loginAs) {
|
||||
await loginPage.loginAs(userName);
|
||||
} else {
|
||||
await loginPage.fillUsername(userName);
|
||||
await loginPage.signup();
|
||||
}
|
||||
};
|
||||
|
||||
test.describe("Admin onboarding flow", () => {
|
||||
test("Create and delete flow", async ({ page }) => {
|
||||
await login({ page, userName: "HR specialist" });
|
||||
|
||||
const homePage = new HomePage(page);
|
||||
await homePage.createEmployee("Paul");
|
||||
await homePage.createEmployee("Sean");
|
||||
await homePage.expectEmployee(["Sean", "admin"]);
|
||||
await homePage.expectEmployee(["Paul", "admin"]);
|
||||
await homePage.deleteEmployee("Sean");
|
||||
await homePage.expectEmployeeDeleted("Sean");
|
||||
});
|
||||
|
||||
test("Onboard flow", async ({ page }) => {
|
||||
const adminUser = "HR specialist";
|
||||
const writerUser = "Invitee";
|
||||
await login({ page, userName: adminUser });
|
||||
|
||||
const homePage = new HomePage(page);
|
||||
await homePage.createEmployee("Paul");
|
||||
await homePage.expectEmployee(["Paul", "admin"]);
|
||||
await homePage.navigateToEmployeeOnboardingPage("Paul");
|
||||
const onboardingPage = new EmployeeOnboardingPage(page);
|
||||
|
||||
// create invitation
|
||||
const invitation = await onboardingPage.getShareLink();
|
||||
await onboardingPage.logout();
|
||||
|
||||
//fill out by invitee (writer)
|
||||
await login({ page, userName: writerUser });
|
||||
await page.goto(invitation);
|
||||
await page.waitForTimeout(1000);
|
||||
await homePage.expectEmployee(["Paul", "write"]);
|
||||
await homePage.navigateToEmployeeOnboardingPage("Paul");
|
||||
await onboardingPage.expectEmployeeName("Paul");
|
||||
await onboardingPage.fillPersonalDetailsCardAndSave(
|
||||
"123-45-6789",
|
||||
"123 Elm Street",
|
||||
);
|
||||
await onboardingPage.fillUploadCardAndSave(
|
||||
"./public/jazz-logo-low-res.jpg",
|
||||
);
|
||||
|
||||
// invitee cannot confirm the onboarding completion
|
||||
expect(onboardingPage.finalConfirmationButton.isDisabled()).toBeTruthy();
|
||||
|
||||
// final confirmation step by admin
|
||||
await onboardingPage.logout();
|
||||
await login({ page, userName: adminUser, loginAs: true });
|
||||
|
||||
await homePage.expectEmployee(["Paul", "admin"]);
|
||||
await homePage.navigateToEmployeeOnboardingPage("Paul");
|
||||
await scrollToBottom(page);
|
||||
await onboardingPage.finalConfirmationButton.click();
|
||||
await onboardingPage.backButton.click();
|
||||
await homePage.expectOnboardingCompleteForEmployee("Paul");
|
||||
});
|
||||
});
|
||||
92
examples/onboarding/tests/pages/EmployeeOnboardingPage.ts
Normal file
92
examples/onboarding/tests/pages/EmployeeOnboardingPage.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Locator, Page, expect } from "@playwright/test";
|
||||
|
||||
export class EmployeeOnboardingPage {
|
||||
readonly page: Page;
|
||||
readonly shareButton: Locator;
|
||||
readonly backButton: Locator;
|
||||
readonly logoutButton: Locator;
|
||||
readonly finalConfirmationButton: Locator;
|
||||
readonly fileInput: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.shareButton = page.getByRole("button", {
|
||||
name: /invite a co-worker/i,
|
||||
});
|
||||
this.backButton = page.getByRole("button", {
|
||||
name: /back/i,
|
||||
});
|
||||
this.logoutButton = page.getByRole("button", {
|
||||
name: /log out/i,
|
||||
});
|
||||
this.finalConfirmationButton = this.page.getByRole("button", {
|
||||
name: /confirmation by admin/i,
|
||||
});
|
||||
|
||||
this.fileInput = page.getByTestId("file-upload");
|
||||
}
|
||||
|
||||
async uploadFile(value: string) {
|
||||
// Start waiting for file chooser before clicking. Note no await.
|
||||
const fileChooserPromise = this.page.waitForEvent("filechooser");
|
||||
|
||||
await this.fileInput.click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(value);
|
||||
}
|
||||
|
||||
async expectEmployeeName(name: string) {
|
||||
await expect(
|
||||
this.page.getByRole("heading", {
|
||||
name: name,
|
||||
}),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async fillPersonalDetailsCardAndSave(ssn: string, address: string) {
|
||||
const nextStepButton = this.page.getByRole("button", {
|
||||
name: /upload step >/i,
|
||||
});
|
||||
await expect(nextStepButton).toBeDisabled();
|
||||
|
||||
const ssnInput = this.page.getByLabel(/Social Security Number/i);
|
||||
await ssnInput.fill(ssn);
|
||||
|
||||
const addressInput = this.page.getByLabel(/Address/i);
|
||||
await addressInput.fill(address);
|
||||
|
||||
// save and hide the button
|
||||
await expect(nextStepButton).toBeEnabled();
|
||||
await nextStepButton.click();
|
||||
await expect(nextStepButton).not.toBeVisible();
|
||||
}
|
||||
|
||||
async fillUploadCardAndSave(file: string) {
|
||||
const nextStepButton = this.page.getByRole("button", {
|
||||
name: /confirmation step >/i,
|
||||
});
|
||||
await expect(nextStepButton).toBeDisabled();
|
||||
|
||||
await this.uploadFile(file);
|
||||
await expect(nextStepButton).toBeEnabled();
|
||||
await nextStepButton.click();
|
||||
await expect(nextStepButton).not.toBeVisible();
|
||||
}
|
||||
|
||||
async getShareLink() {
|
||||
await this.shareButton.click();
|
||||
|
||||
const inviteUrl = await this.page.evaluate(() =>
|
||||
navigator.clipboard.readText(),
|
||||
);
|
||||
|
||||
expect(inviteUrl).toBeTruthy();
|
||||
|
||||
return inviteUrl;
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.logoutButton.click();
|
||||
}
|
||||
}
|
||||
66
examples/onboarding/tests/pages/HomePage.ts
Normal file
66
examples/onboarding/tests/pages/HomePage.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Locator, Page, expect } from "@playwright/test";
|
||||
import { NewEmployeePage } from "./NewEmployeePage";
|
||||
|
||||
export class HomePage {
|
||||
readonly page: Page;
|
||||
readonly newEmployeeLink: Locator;
|
||||
readonly logoutButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.newEmployeeLink = page.getByRole("button", {
|
||||
name: "Add New Employee",
|
||||
});
|
||||
this.logoutButton = page.getByRole("button", {
|
||||
name: "Log Out",
|
||||
});
|
||||
}
|
||||
|
||||
async expectEmployee([name, role]: [string, string]) {
|
||||
const liElement = this.page.locator(
|
||||
`li:has-text("${name}"):has-text("${role}")`,
|
||||
);
|
||||
await expect(liElement).toBeVisible();
|
||||
}
|
||||
|
||||
async deleteEmployee(name: string) {
|
||||
const liElement = this.page.locator(`li:has-text("${name}")`);
|
||||
const deleteIcon = liElement.locator('span:has-text("🗑")');
|
||||
await deleteIcon.click();
|
||||
}
|
||||
|
||||
async expectOnboardingCompleteForEmployee(name: string) {
|
||||
const liElement = this.page.locator(`li:has-text("${name}")`);
|
||||
const completionIcon = liElement.locator('span:has-text("✅")');
|
||||
await expect(completionIcon).toBeVisible();
|
||||
}
|
||||
|
||||
async expectEmployeeDeleted(name: string) {
|
||||
const liElement = this.page.locator(`li:has-text("${name}")`);
|
||||
await expect(liElement).not.toBeVisible();
|
||||
}
|
||||
|
||||
async navigateToEmployeeOnboardingPage(name: string) {
|
||||
await this.page
|
||||
.getByRole("link", {
|
||||
name,
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async navigateToNewEmployee() {
|
||||
await this.newEmployeeLink.click();
|
||||
}
|
||||
|
||||
async createEmployee(name: string) {
|
||||
await this.navigateToNewEmployee();
|
||||
const newEmployeePage = new NewEmployeePage(this.page);
|
||||
|
||||
await newEmployeePage.fillEmployeeName(name);
|
||||
await newEmployeePage.submit();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.logoutButton.click();
|
||||
}
|
||||
}
|
||||
40
examples/onboarding/tests/pages/LoginPage.ts
Normal file
40
examples/onboarding/tests/pages/LoginPage.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Locator, Page, expect } from "@playwright/test";
|
||||
|
||||
export class LoginPage {
|
||||
readonly page: Page;
|
||||
readonly usernameInput: Locator;
|
||||
readonly signupButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.usernameInput = page.getByRole("textbox");
|
||||
this.signupButton = page.getByRole("button", {
|
||||
name: "Sign up",
|
||||
});
|
||||
}
|
||||
|
||||
async goto(url: string) {
|
||||
await this.page.goto(url);
|
||||
}
|
||||
|
||||
async fillUsername(value: string) {
|
||||
await this.usernameInput.clear();
|
||||
await this.usernameInput.fill(value);
|
||||
}
|
||||
|
||||
async loginAs(value: string) {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
name: value,
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async signup() {
|
||||
await this.signupButton.click();
|
||||
}
|
||||
|
||||
async expectLoaded() {
|
||||
await expect(this.signupButton).toBeVisible();
|
||||
}
|
||||
}
|
||||
24
examples/onboarding/tests/pages/NewEmployeePage.ts
Normal file
24
examples/onboarding/tests/pages/NewEmployeePage.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
|
||||
export class NewEmployeePage {
|
||||
readonly page: Page;
|
||||
readonly employeeNameInput: Locator;
|
||||
readonly submitButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.employeeNameInput = page.getByLabel(/employee name/i);
|
||||
this.submitButton = page.getByRole("button", {
|
||||
name: /create employee/i,
|
||||
});
|
||||
}
|
||||
|
||||
async fillEmployeeName(value: string) {
|
||||
await this.employeeNameInput.clear();
|
||||
await this.employeeNameInput.fill(value);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await this.submitButton.click();
|
||||
}
|
||||
}
|
||||
27
examples/onboarding/tsconfig.json
Normal file
27
examples/onboarding/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
11
examples/onboarding/tsconfig.node.json
Normal file
11
examples/onboarding/tsconfig.node.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
17
examples/onboarding/vite.config.ts
Normal file
17
examples/onboarding/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import { defineConfig } from "vite";
|
||||
import topLevelAwait from "vite-plugin-top-level-await";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), topLevelAwait()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
minify: false,
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,12 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.16",
|
||||
"version": "0.0.17",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -12,8 +12,8 @@
|
||||
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.115
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
- jazz-browser-media-images@0.8.19
|
||||
|
||||
## 0.0.114
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Jazz Rate-My-Pet List Example
|
||||
|
||||
Live version: https://example-pets.jazz.tools
|
||||
Live version: https://pets-demo.jazz.tools/
|
||||
|
||||
## Installing & running the example locally
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.114",
|
||||
"version": "0.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -19,9 +19,9 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-browser-media-images": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-browser-media-images": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-run": "workspace:0.8.18",
|
||||
"jazz-run": "workspace:0.8.19",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.114
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.0.113
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Jazz Todo List Example
|
||||
|
||||
Live version: https://example-todo.jazz.tools
|
||||
Live version: https://todo-demo.jazz.tools/
|
||||
|
||||
## Installing & running the example locally
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.113",
|
||||
"version": "0.0.114",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -16,8 +16,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import Link from "next/link";
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
variant?: "primary" | "secondary" | "tertiary";
|
||||
size?: "md" | "lg";
|
||||
size?: "sm" | "md" | "lg";
|
||||
href?: string;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export function Button(props: ButtonProps) {
|
||||
} = props;
|
||||
|
||||
const sizeClasses = {
|
||||
sm: "text-sm py-1 px-2",
|
||||
md: "py-2 px-3",
|
||||
lg: "md:text-lg py-2 px-3 md:px-8 md:py-3",
|
||||
};
|
||||
@@ -34,7 +35,7 @@ export function Button(props: ButtonProps) {
|
||||
|
||||
const classNames = clsx(
|
||||
className,
|
||||
"inline-block rounded-lg text-center transition-colors",
|
||||
"inline-flex items-center gap-2 rounded-lg text-center transition-colors",
|
||||
sizeClasses[size],
|
||||
variantClasses[variant],
|
||||
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
|
||||
|
||||
@@ -1,48 +1,305 @@
|
||||
import { ClerkFullLogo } from "@/components/icons/ClerkFullLogo";
|
||||
import { NextjsLogo } from "@/components/icons/NextjsLogo";
|
||||
import { ReactLogo } from "@/components/icons/ReactLogo";
|
||||
import { ReactNativeLogo } from "@/components/icons/ReactNativeLogo";
|
||||
import { Button } from "gcmp-design-system/src/app/components/atoms/Button";
|
||||
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
|
||||
import { CloudUploadIcon, FingerprintIcon, KeyRoundIcon } from "lucide-react";
|
||||
|
||||
type Example = {
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
illustration?: React.ReactNode;
|
||||
tech?: string[];
|
||||
features?: string[];
|
||||
demoUrl?: string;
|
||||
};
|
||||
|
||||
const tech = {
|
||||
react: "React",
|
||||
nextjs: "Next.js",
|
||||
reactNative: "React Native",
|
||||
};
|
||||
|
||||
const features = {
|
||||
fileUpload: "File upload",
|
||||
passkey: "Passkey auth",
|
||||
clerk: "Clerk auth",
|
||||
inviteLink: "Invite link",
|
||||
};
|
||||
|
||||
const ChatIllustration = () => (
|
||||
<div className="p-4 flex flex-col gap-4 justify-center h-full">
|
||||
<div className="flex flex-col gap-1 items-end">
|
||||
<p className="text-2xs">Sebastian</p>
|
||||
<p className="inline-block text-xs py-1.5 px-3 rounded-full bg-blue text-white">
|
||||
No one likes jazz. Not even you.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 items-start">
|
||||
<p className="text-2xs">Mia</p>
|
||||
<p className="inline-block text-xs py-1.5 px-3 rounded-full bg-stone-200 text-stone-900 dark:bg-white">
|
||||
I do like jazz now, because of you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ClerkIllustration = () => (
|
||||
<div className="flex items-center justify-center h-full p-8">
|
||||
<ClerkFullLogo className="w-36 h-auto" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const MusicIllustration = () => (
|
||||
<div className="flex flex-col items-center justify-center h-full p-8">
|
||||
<div className="py-3 px-4 border border-dashed border-blue dark:border-blue-500 rounded-lg flex gap-2 flex-col items-center">
|
||||
<CloudUploadIcon
|
||||
size={30}
|
||||
strokeWidth={1.5}
|
||||
className="stroke-blue mx-auto dark:stroke-blue-500"
|
||||
/>
|
||||
<p className="whitespace-nowrap text-xs text-stone-900 dark:text-white">
|
||||
tortured-poets-department.mp3
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const BookShelfIllustration = () => (
|
||||
<div className="h-full p-6 md:p-5">
|
||||
<div className="flex justify-between items-baseline">
|
||||
<p className="font-display font-medium tracking-tight text-sm text-stone-900 dark:text-white">
|
||||
Your book shelf
|
||||
</p>
|
||||
|
||||
<p className="bg-blue-100 text-blue-800 py-1 px-3 rounded-full font-medium text-xs">
|
||||
Add book
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative grid grid-cols-3 gap-4 mt-3">
|
||||
{["malibu.jpg", "pathless.jpg", "upgrade.jpg"].map((book) => (
|
||||
<img
|
||||
key={book}
|
||||
src={`/book-covers/${book}`}
|
||||
alt=""
|
||||
className="w-full h-full object-cover rounded-r-md shadow-sm border dark:border-none"
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="absolute bottom-0 w-full h-10 bg-gradient-to-t from-white dark:from-stone-925 to-transparent md:hidden" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const PetIllustration = () => (
|
||||
<div className="h-full p-4 bg-[url('/dog.jpg')] bg-cover bg-center p-4 flex items-end">
|
||||
<div className="inline-flex justify-center gap-1 mx-auto">
|
||||
{["😍", "😮", "🤩", "😂", "👍"].map((emoji) => (
|
||||
<button
|
||||
type="button"
|
||||
key={emoji}
|
||||
className="size-6 rounded shadow-sm bg-white leading-none hover:bg-stone-100"
|
||||
>
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const reactExamples = [
|
||||
{
|
||||
name: "Chat",
|
||||
slug: "chat",
|
||||
description: "A simple app that creates a chat room with a shareable link.",
|
||||
tech: [tech.react],
|
||||
demoUrl: "https://chat.jazz.tools",
|
||||
illustration: <ChatIllustration />,
|
||||
},
|
||||
{
|
||||
name: "Chat",
|
||||
slug: "chat-clerk",
|
||||
description:
|
||||
"Exactly like the chat app, but it uses Clerk for authentication.",
|
||||
tech: [tech.react],
|
||||
features: [features.clerk],
|
||||
demoUrl: "https://chat-clerk-demo.jazz.tools",
|
||||
illustration: <ClerkIllustration />,
|
||||
},
|
||||
{
|
||||
name: "Music player",
|
||||
slug: "music-player",
|
||||
description:
|
||||
"Upload your favorite songs, and share them with your friends.",
|
||||
tech: [tech.react],
|
||||
features: [features.fileUpload],
|
||||
demoUrl: "https://music-demo.jazz.tools",
|
||||
illustration: <MusicIllustration />,
|
||||
},
|
||||
{
|
||||
name: "Rate my pet",
|
||||
slug: "pets",
|
||||
description:
|
||||
"Upload a photo of your pet, and invite your friends to react to it.",
|
||||
tech: [tech.react],
|
||||
features: [features.fileUpload, features.inviteLink],
|
||||
demoUrl: "https://pets-demo.jazz.tools",
|
||||
illustration: <PetIllustration />,
|
||||
},
|
||||
{
|
||||
name: "Todo list",
|
||||
slug: "todo",
|
||||
description: "A todo list where you can collaborate with invited guests.",
|
||||
tech: [tech.react],
|
||||
features: [features.inviteLink],
|
||||
demoUrl: "https://todo-demo.jazz.tools",
|
||||
illustration: (
|
||||
<div className="h-full w-full bg-cover bg-[url('/todo.jpg')] bg-left-bottom"></div>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Password manager",
|
||||
slug: "password-manager",
|
||||
description: "A secure password manager, using Passkey for authentication.",
|
||||
tech: [tech.react],
|
||||
features: [features.passkey],
|
||||
demoUrl: "https://passwords-demo.jazz.tools",
|
||||
illustration: (
|
||||
<div className="flex bg-stone-100 h-full flex-col items-center justify-center dark:bg-transparent">
|
||||
<div className="p-4 flex flex-col items-center gap-3 rounded-md shadow-xl shadow-stone-400/20 bg-white dark:shadow-none">
|
||||
<FingerprintIcon
|
||||
size={36}
|
||||
strokeWidth={0.75}
|
||||
className="stroke-red-600"
|
||||
/>
|
||||
<p className="text-xs dark:text-stone-900">Continue with Touch ID</p>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const nextExamples = [
|
||||
{
|
||||
name: "Book shelf",
|
||||
slug: "book-shelf",
|
||||
description:
|
||||
"Track and rate the books you read, readable by everyone with the link.",
|
||||
tech: [tech.nextjs],
|
||||
features: [features.fileUpload],
|
||||
demoUrl: "https://books-demo.jazz.tools",
|
||||
imageUrl: "/books.jpg",
|
||||
illustration: <BookShelfIllustration />,
|
||||
},
|
||||
];
|
||||
|
||||
const rnExamples: Example[] = [
|
||||
{
|
||||
name: "Chat",
|
||||
slug: "chat-rn",
|
||||
description: "A simple app that creates a chat room with a shareable link.",
|
||||
tech: [tech.reactNative],
|
||||
illustration: <ChatIllustration />,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Chat",
|
||||
slug: "chat-rn-clerk",
|
||||
description: "Exactly like the React Native chat app, with Clerk for auth.",
|
||||
tech: [tech.reactNative],
|
||||
features: [features.clerk],
|
||||
illustration: <ClerkIllustration />,
|
||||
},
|
||||
];
|
||||
const categories = [
|
||||
{
|
||||
name: "React",
|
||||
logo: ReactLogo,
|
||||
examples: reactExamples,
|
||||
},
|
||||
{
|
||||
name: "Next.js",
|
||||
logo: NextjsLogo,
|
||||
examples: nextExamples,
|
||||
},
|
||||
{
|
||||
name: "React Native",
|
||||
logo: ReactNativeLogo,
|
||||
examples: rnExamples,
|
||||
},
|
||||
];
|
||||
|
||||
function Example({ example }: { example: Example }) {
|
||||
const { name, slug, tech, features, description, demoUrl, illustration } =
|
||||
example;
|
||||
const githubUrl = `https://github.com/gardencmp/jazz/tree/main/examples/${slug}`;
|
||||
|
||||
return (
|
||||
<div className="border bg-stone-50 shadow-sm p-3 flex flex-col gap-3 rounded-lg md:gap-4 dark:bg-stone-950">
|
||||
<div className="aspect-[16/9] overflow-hidden w-full rounded-md bg-white border dark:bg-stone-925 sm:aspect-[2/1] md:aspect-[3/2]">
|
||||
{illustration}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-2">
|
||||
<h2 className="font-medium text-stone-900 dark:text-white leading-none">
|
||||
{name}
|
||||
</h2>
|
||||
<div className="flex gap-1">
|
||||
{tech?.map((tech) => (
|
||||
<p
|
||||
className="bg-green-50 border border-green-500 text-green-600 rounded-full py-0.5 px-2 text-xs dark:bg-green-800 dark:text-green-200 dark:border-green-700"
|
||||
key={tech}
|
||||
>
|
||||
{tech}
|
||||
</p>
|
||||
))}
|
||||
{features?.map((feature) => (
|
||||
<p
|
||||
className="bg-pink-50 border border-pink-500 text-pink-600 rounded-full py-0.5 px-2 text-xs dark:bg-pink-800 dark:text-pink-200 dark:border-pink-700"
|
||||
key={feature}
|
||||
>
|
||||
{feature}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm">{description}</p>
|
||||
<div className="flex gap-2">
|
||||
<Button href={githubUrl} variant="secondary" size="sm">
|
||||
View code
|
||||
</Button>
|
||||
{demoUrl && (
|
||||
<Button href={demoUrl} variant="secondary" size="sm">
|
||||
View demo
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const examples = [
|
||||
{
|
||||
name: "Chat",
|
||||
slug: "chat",
|
||||
},
|
||||
{
|
||||
name: "Chat (with Clerk for auth)",
|
||||
slug: "chat-clerk",
|
||||
},
|
||||
{
|
||||
name: "Music player",
|
||||
slug: "music-player",
|
||||
},
|
||||
{
|
||||
name: "Pets",
|
||||
slug: "pets",
|
||||
},
|
||||
{
|
||||
name: "Todo",
|
||||
slug: "todo",
|
||||
},
|
||||
{
|
||||
name: "Password manager",
|
||||
slug: "password-manager",
|
||||
},
|
||||
{
|
||||
name: "Book shelf",
|
||||
slug: "book-shelf",
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<h1>Example Apps</h1>
|
||||
<ul>
|
||||
{examples.map(({ name, slug }) => (
|
||||
<li key={name}>
|
||||
<a
|
||||
href={`https://github.com/gardencmp/jazz/tree/main/examples/${slug}`}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{categories.map((category) => (
|
||||
<div className="my-12" key={category.name}>
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<category.logo className="h-8 w-8" />
|
||||
<h2 className="my-0">{category.name}</h2>
|
||||
</div>
|
||||
|
||||
<div className="not-prose grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-8">
|
||||
{category.examples.map((example) => (
|
||||
<Example key={example.slug} example={example} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function Page() {
|
||||
<Link
|
||||
href={product.url}
|
||||
key={product.url}
|
||||
className="group border bg-stone-50 shadow-sm p-3 flex flex-col gap-3 rounded-lg md:p-4 md:gap-4 dark:bg-stone-900"
|
||||
className="group border bg-stone-50 shadow-sm p-3 flex flex-col gap-3 rounded-lg md:gap-4 dark:bg-stone-950"
|
||||
>
|
||||
<Image
|
||||
className="rounded-md border dark:border-0"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { clsx } from "clsx";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export function SideNavItem({
|
||||
@@ -15,6 +18,7 @@ export function SideNavItem({
|
||||
className,
|
||||
"py-1 flex items-center hover:transition-colors",
|
||||
);
|
||||
const path = usePathname();
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
@@ -24,6 +28,9 @@ export function SideNavItem({
|
||||
classes,
|
||||
href &&
|
||||
"hover:text-black dark:hover:text-stone-200 transition-colors hover:transition-none",
|
||||
{
|
||||
"text-black": path === href,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
51
homepage/homepage/components/icons/ClerkFullLogo.tsx
Normal file
51
homepage/homepage/components/icons/ClerkFullLogo.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { clsx } from "clsx";
|
||||
import React from "react";
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
export function ClerkFullLogo(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
className={clsx(props.className, "text-black dark:text-white")}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="110"
|
||||
height="32"
|
||||
viewBox="0 0 110 32"
|
||||
fill="none"
|
||||
>
|
||||
<ellipse
|
||||
cx="16.0003"
|
||||
cy="16"
|
||||
rx="4.99998"
|
||||
ry="5"
|
||||
fill="#6C47FF"
|
||||
fillOpacity="1"
|
||||
style={{
|
||||
fill: "color(display-p3 0.4235 0.2784 1.0000)",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M25.0091 27.8382C25.4345 28.2636 25.3918 28.9679 24.8919 29.3027C22.3488 31.0062 19.2899 31.9997 15.999 31.9997C12.7082 31.9997 9.64934 31.0062 7.10615 29.3027C6.60632 28.9679 6.56361 28.2636 6.989 27.8382L10.6429 24.1843C10.9732 23.854 11.4855 23.8019 11.9012 24.0148C13.1302 24.6445 14.5232 24.9997 15.999 24.9997C17.4749 24.9997 18.8678 24.6445 20.0969 24.0148C20.5126 23.8019 21.0249 23.854 21.3552 24.1843L25.0091 27.8382Z"
|
||||
fill="#6C47FF"
|
||||
fillOpacity="1"
|
||||
style={{
|
||||
fill: "color(display-p3 0.4235 0.2784 1.0000)",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M24.8928 2.697C25.3926 3.0318 25.4353 3.73609 25.0099 4.16149L21.356 7.81544C21.0258 8.14569 20.5134 8.19785 20.0978 7.98491C18.8687 7.35525 17.4758 7 15.9999 7C11.0294 7 6.99997 11.0294 6.99997 16C6.99997 17.4759 7.35522 18.8688 7.98488 20.0979C8.19782 20.5136 8.14565 21.0259 7.81541 21.3561L4.16147 25.0101C3.73607 25.4355 3.03178 25.3927 2.69698 24.8929C0.993522 22.3497 0 19.2909 0 16C0 7.16344 7.16341 0 15.9999 0C19.2908 0 22.3496 0.993529 24.8928 2.697Z"
|
||||
fill="#BAB1FF"
|
||||
style={{
|
||||
fill: "color(display-p3 0.7294 0.6941 1.0000)",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M100.405 21.2489C100.421 21.2324 100.442 21.2231 100.465 21.2231C100.493 21.2231 100.518 21.2375 100.533 21.2613L105.275 28.8821C105.321 28.9554 105.401 29 105.487 29L109.75 29C109.946 29 110.066 28.7848 109.963 28.6183L103.457 18.1226C103.399 18.0278 103.41 17.9056 103.485 17.823L109.752 10.908C109.898 10.7473 109.784 10.4901 109.567 10.4901H105.12C105.05 10.4901 104.983 10.5194 104.936 10.5711L97.6842 18.4755C97.5301 18.6435 97.25 18.5345 97.25 18.3065V3.25C97.25 3.11193 97.138 3 97 3H93.25C93.1119 3 93 3.11193 93 3.25V28.75C93 28.8881 93.1119 29 93.25 29L97 29C97.138 29 97.25 28.8881 97.25 28.75V24.7373C97.25 24.6741 97.2739 24.6132 97.317 24.567L100.405 21.2489ZM52.2502 3.25C52.2502 3.11193 52.3621 3 52.5002 3H56.2501C56.3882 3 56.5001 3.11193 56.5001 3.25V28.75C56.5001 28.8881 56.3882 29 56.2501 29H52.5002C52.3621 29 52.2502 28.8881 52.2502 28.75V3.25ZM46.958 23.5912C46.8584 23.5052 46.7094 23.5117 46.6137 23.602C46.0293 24.1537 45.3447 24.595 44.5947 24.9028C43.7719 25.2407 42.8873 25.4108 41.995 25.4028C41.2415 25.4252 40.4913 25.2963 39.7906 25.0241C39.09 24.7519 38.4537 24.3422 37.9209 23.8202C36.9531 22.8322 36.396 21.4215 36.396 19.7399C36.396 16.3735 38.6356 14.0709 41.995 14.0709C42.896 14.0585 43.7888 14.241 44.6094 14.6052C45.3533 14.9355 46.0214 15.4077 46.5748 15.9934C46.6694 16.0936 46.8266 16.1052 46.9309 16.015L49.4625 13.8244C49.5659 13.7349 49.5785 13.5786 49.4873 13.4767C47.583 11.3488 44.5997 10.25 41.7627 10.25C36.0506 10.25 32.0003 14.1031 32.0003 19.7719C32.0003 22.5756 33.0069 24.9365 34.7044 26.6036C36.402 28.2707 38.8203 29.25 41.6108 29.25C45.1097 29.25 47.9259 27.9082 49.577 26.187C49.6739 26.086 49.6632 25.9252 49.5572 25.8338L46.958 23.5912ZM77.1575 20.9877C77.1436 21.1129 77.0371 21.2066 76.9111 21.2066H63.7746C63.615 21.2066 63.4961 21.3546 63.5377 21.5087C64.1913 23.9314 66.1398 25.3973 68.7994 25.3973C69.6959 25.4161 70.5846 25.2317 71.3968 24.8582C72.1536 24.5102 72.8249 24.0068 73.3659 23.3828C73.4314 23.3073 73.5454 23.2961 73.622 23.3602L76.2631 25.6596C76.3641 25.7476 76.3782 25.8999 76.2915 26.0021C74.697 27.8832 72.1135 29.25 68.5683 29.25C63.1142 29.25 59.0001 25.4731 59.0001 19.7348C59.0001 16.9197 59.9693 14.559 61.5847 12.8921C62.4374 12.0349 63.4597 11.3584 64.5882 10.9043C65.7168 10.4502 66.9281 10.2281 68.1473 10.2517C73.6753 10.2517 77.25 14.1394 77.25 19.5075C77.2431 20.0021 77.2123 20.4961 77.1575 20.9877ZM63.6166 17.5038C63.5702 17.6581 63.6894 17.8084 63.8505 17.8084H72.5852C72.7467 17.8084 72.8659 17.6572 72.8211 17.5021C72.2257 15.4416 70.7153 14.0666 68.3696 14.0666C67.6796 14.0447 66.993 14.1696 66.3565 14.4326C65.7203 14.6957 65.149 15.0908 64.6823 15.5907C64.1914 16.1473 63.8285 16.7998 63.6166 17.5038ZM90.2473 10.2527C90.3864 10.2512 90.5 10.3636 90.5 10.5027V14.7013C90.5 14.8469 90.3762 14.9615 90.2311 14.9508C89.8258 14.9207 89.4427 14.8952 89.1916 14.8952C85.9204 14.8952 84 17.1975 84 20.2195V28.75C84 28.8881 83.8881 29 83.75 29H80C79.862 29 79.75 28.8881 79.75 28.75V10.7623C79.75 10.6242 79.862 10.5123 80 10.5123H83.75C83.8881 10.5123 84 10.6242 84 10.7623V13.287C84 13.3013 84.0116 13.3128 84.0258 13.3128C84.034 13.3128 84.0416 13.3089 84.0465 13.3024C85.5124 11.3448 87.676 10.2559 89.9617 10.2559L90.2473 10.2527Z"
|
||||
className="fill-[#131316] dark:fill-white"
|
||||
fillOpacity="1"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
11
homepage/homepage/components/icons/NextjsLogo.tsx
Normal file
11
homepage/homepage/components/icons/NextjsLogo.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { SiNextdotjs } from "@icons-pack/react-simple-icons";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
export function NextjsLogo(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<SiNextdotjs
|
||||
{...props}
|
||||
className={clsx(props.className, "fill-black dark:fill-white")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
BIN
homepage/homepage/public/book-covers/malibu.jpg
Normal file
BIN
homepage/homepage/public/book-covers/malibu.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
BIN
homepage/homepage/public/book-covers/pathless.jpg
Normal file
BIN
homepage/homepage/public/book-covers/pathless.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
homepage/homepage/public/book-covers/upgrade.jpg
Normal file
BIN
homepage/homepage/public/book-covers/upgrade.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
BIN
homepage/homepage/public/dog.jpg
Normal file
BIN
homepage/homepage/public/dog.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
homepage/homepage/public/todo.jpg
Normal file
BIN
homepage/homepage/public/todo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"typescript": "^5.3.3"
|
||||
"cojson": "workspace:0.8.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitest/browser": "^0.34.1",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"vitest": "1.5.3",
|
||||
"webdriverio": "^8.15.0"
|
||||
"webdriverio": "^8.15.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "tsc --watch --sourceMap --outDir dist",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^8.5.2",
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# cojson
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9c2aadb: Set a CoValue as errored per peer after first error
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.3",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
PriorityBasedMessageQueue,
|
||||
QueueEntry,
|
||||
} from "./PriorityBasedMessageQueue.js";
|
||||
import { TryAddTransactionsError } from "./coValueCore.js";
|
||||
import { RawCoID } from "./ids.js";
|
||||
import { CO_VALUE_PRIORITY } from "./priority.js";
|
||||
import { Peer, SyncMessage } from "./sync.js";
|
||||
@@ -33,6 +34,8 @@ export class PeerState {
|
||||
readonly optimisticKnownStates: PeerKnownStates;
|
||||
readonly toldKnownState: Set<RawCoID> = new Set();
|
||||
|
||||
readonly erroredCoValues: Map<RawCoID, TryAddTransactionsError> = new Map();
|
||||
|
||||
get id() {
|
||||
return this.peer.id;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,12 @@ export class SyncManager {
|
||||
const coValueEntry = this.local.coValues[id];
|
||||
|
||||
for (const peer of eligiblePeers) {
|
||||
if (peer.erroredCoValues.has(id)) {
|
||||
console.error(
|
||||
`Skipping load on errored coValue ${id} from peer ${peer.id}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
await peer.pushOutgoingMessage({
|
||||
action: "load",
|
||||
id: id,
|
||||
@@ -157,6 +163,12 @@ export class SyncManager {
|
||||
}
|
||||
|
||||
async handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
||||
if (peer.erroredCoValues.has(msg.id)) {
|
||||
console.error(
|
||||
`Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// TODO: validate
|
||||
switch (msg.action) {
|
||||
case "load":
|
||||
@@ -629,6 +641,7 @@ export class SyncManager {
|
||||
"our last known tx idx now: " +
|
||||
coValue.sessionLogs.get(sessionID)?.transactions.length,
|
||||
);
|
||||
peer.erroredCoValues.set(msg.id, result.error);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -666,6 +679,11 @@ export class SyncManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We do send a correction/ack message before syncing to give an immediate
|
||||
* response to the peers that are waiting for confirmation that a coValue is
|
||||
* fully synced
|
||||
*/
|
||||
await this.syncCoValue(coValue);
|
||||
}
|
||||
|
||||
@@ -711,6 +729,7 @@ export class SyncManager {
|
||||
// let blockingSince = performance.now();
|
||||
for (const peer of this.peersInPriorityOrder()) {
|
||||
if (peer.closed) continue;
|
||||
if (peer.erroredCoValues.has(coValue.id)) continue;
|
||||
// if (performance.now() - blockingSince > 5) {
|
||||
// await new Promise<void>((resolve) => {
|
||||
// setTimeout(resolve, 0);
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-browser@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "jazz-browser-auth-clerk",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"jazz-browser": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18"
|
||||
"cojson": "workspace:0.8.19",
|
||||
"jazz-browser": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-browser@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser-media-images",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
@@ -8,8 +8,8 @@
|
||||
"dependencies": {
|
||||
"@types/image-blob-reduce": "^4.1.1",
|
||||
"image-blob-reduce": "^4.1.0",
|
||||
"jazz-browser": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"jazz-browser": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"pica": "^9.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- cojson-storage-indexeddb@0.8.19
|
||||
- cojson-transport-ws@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scure/bip39": "^1.3.0",
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson-storage-indexeddb": "workspace:0.8.18",
|
||||
"cojson-transport-ws": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson-storage-indexeddb": "workspace:0.8.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-autosub
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- cojson-transport-ws@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
"types": "src/index.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"cojson-transport-ws": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-browser-auth-clerk@0.8.19
|
||||
- jazz-react@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "jazz-react-auth-clerk",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.18",
|
||||
"jazz-browser-auth-clerk": "workspace:0.8.18",
|
||||
"jazz-react": "workspace:0.8.18",
|
||||
"jazz-tools": "workspace:0.8.18"
|
||||
"cojson": "workspace:0.8.19",
|
||||
"jazz-browser-auth-clerk": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "jazz-react-native-media-images",
|
||||
"version": "0.8.14",
|
||||
"version": "0.8.15",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jazz-tools": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"jazz-tools": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.10",
|
||||
"expo-file-system": "^17.0.1",
|
||||
"react-native": "~0.74.5"
|
||||
"react-native": "~0.74.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@bam.tech/react-native-image-resizer": "*",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- cojson-transport-ws@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native",
|
||||
"version": "0.8.18",
|
||||
"version": "0.8.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -18,8 +18,7 @@
|
||||
"@scure/bip39": "^1.3.0",
|
||||
"cojson": "workspace:*",
|
||||
"cojson-transport-ws": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"jazz-tools": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-community/netinfo": "*",
|
||||
@@ -31,7 +30,8 @@
|
||||
"@react-native-community/netinfo": "^11.3.1",
|
||||
"expo-linking": "~6.3.1",
|
||||
"expo-secure-store": "~13.0.2",
|
||||
"react-native": "~0.74.5"
|
||||
"react-native": "~0.74.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "tsc --watch --sourceMap --outDir dist",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-react
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9c2aadb]
|
||||
- cojson@0.8.19
|
||||
- jazz-browser@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user