Compare commits

...

14 Commits

Author SHA1 Message Date
Mohamad
1ae232d9c0 Merge branch 'build' 2025-01-03 12:49:38 +01:00
Mohamad
9aeea66f22 build yml
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 17s
2025-01-03 12:44:39 +01:00
Mohamad
32fa7f7782 smth
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 13s
2025-01-03 11:47:03 +01:00
Mohamad
7ecd92a06c kill me pt2 (typo)
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 17s
2025-01-03 11:44:09 +01:00
Mohamad
189a24b230 dockerfile bugfix (kill me)
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 14s
2025-01-03 11:43:21 +01:00
Mohamad.Elsena
482aa13e76 binary name fix
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 17s
2025-01-03 11:40:16 +01:00
Mohamad
7d374b93c5 rust version update
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 1m14s
2025-01-03 10:45:33 +01:00
Mohamad
df4808bf38 Serve Frontend with Actix
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 15s
2025-01-03 10:43:38 +01:00
Mohamad.Elsena
efd11a39b9 added node adapter
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 14s
2025-01-03 10:34:31 +01:00
Mohamad
d26b4c39aa typo
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 44s
2025-01-03 10:28:57 +01:00
Mohamad.Elsena
14c2a123cf gitea workflow
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 17s
2025-01-03 10:26:04 +01:00
Mohamad.Elsena
98afc28773 added basic navbar 2025-01-02 15:53:08 +01:00
Mohamad.Elsena
2436036d92 test db 2025-01-02 15:49:33 +01:00
Mohamad.Elsena
0f2b22941c basic styling init 2025-01-02 15:49:22 +01:00
14 changed files with 559 additions and 223 deletions

View File

@ -1,34 +0,0 @@
name: Build and Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone the repository
uses: actions/checkout@v3
- name: Set up Docker
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Build and Push Docker Image
run: |
docker build -t your-dockerhub-username/formies-combined .
docker tag your-dockerhub-username/formies-combined:latest
docker push your-dockerhub-username/formies-combined:latest
- name: Deploy to Server (optional)
run: |
ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }} << 'EOF'
docker pull your-dockerhub-username/formies-combined:latest
docker stop formies || true
docker rm formies || true
docker run -d --name formies -p 8080:8080 your-dockerhub-username/formies-combined:latest
EOF

View File

@ -0,0 +1,31 @@
name: Build and Push Docker Image
on:
push:
branches:
- build
jobs:
build_and_push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Docker
run: |
sudo apt-get update
sudo apt-get install -y docker.io
- name: Build Docker image
run: |
docker build -t git.vinylnostalgia.com/mo/formies:latest .
- name: Push Docker image to Gitea
env:
GITEA_USERNAME: ${{ secrets.ME_USERNAME }}
GITEA_PASSWORD: ${{ secrets.ME_PASSWORD }}
run: |
echo $GITEA_PASSWORD | docker login git.vinylnostalgia.com -u $GITEA_USERNAME --password-stdin
docker push git.vinylnostalgia.com/mo/formies:latest

View File

@ -1,51 +1,20 @@
# Stage 1: Build Frontend
# Stage 1: Build the Svelte frontend
FROM node:18 as frontend-builder
WORKDIR /frontend
# Copy frontend package files
COPY frontend/package.json frontend/package-lock.json ./
WORKDIR /app/frontend
COPY frontend/ .
RUN npm install
# Copy the rest of the frontend source code
COPY frontend ./
# Build the frontend
RUN npm run build
# Stage 2: Build Backend
FROM rust:1.72 as backend-builder
WORKDIR /backend
# Copy backend files
COPY backend/Cargo.toml backend/Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
# Stage 2: Build the Rust backend
FROM rust:1.83 as backend-builder
WORKDIR /app/backend
COPY backend/ .
RUN cargo build --release
# Copy the actual backend source code
COPY backend/src ./src
RUN cargo build --release
# Stage 3: Combine and Serve
# Final Stage: Combine frontend and backend
FROM debian:bullseye-slim
# Install dependencies for running Rust binaries
RUN apt-get update && apt-get install -y libssl-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy backend binary
COPY --from=backend-builder /backend/target/release/backend .
# Copy frontend static files
COPY --from=frontend-builder /frontend/public ./frontend/public
# Expose port
COPY --from=frontend-builder /app/frontend/build ./frontend/dist
COPY --from=backend-builder /app/backend/target/release/formies_be ./formies_be
EXPOSE 8080
# Run the backend (serving static files and API)
CMD ["./backend"]
#docker build -t your-dockerhub-username/formies-combined .
#docker push your-dockerhub-username/formies-combined:latest

Binary file not shown.

View File

@ -1,4 +1,5 @@
use actix_cors::Cors;
use actix_files as fs;
use actix_web::{web, App, HttpServer};
use std::sync::{Arc, Mutex};
@ -44,6 +45,7 @@ async fn main() -> std::io::Result<()> {
.allow_any_method(),
)
.app_data(web::Data::new(db.clone()))
.service(fs::Files::new("/", "./frontend/dist").index_file("index.html"))
.route("/login", web::post().to(handlers::login)) // Public: Login
.route(
"/forms/{id}/submissions",

View File

@ -14,6 +14,7 @@
"devDependencies": {
"@eslint/compat": "^1.2.3",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"eslint": "^9.7.0",
@ -756,6 +757,112 @@
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/plugin-commonjs": {
"version": "28.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz",
"integrity": "sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"fdir": "^6.2.0",
"is-reference": "1.2.1",
"magic-string": "^0.30.3",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=16.0.0 || 14 >= 14.17"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/@rollup/plugin-json": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.1.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.0.tgz",
"integrity": "sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
@ -1035,6 +1142,22 @@
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/adapter-node": {
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.11.tgz",
"integrity": "sha512-lR7/dfUaKFf3aI408KRDy/BVDYoqUws7zNOJz2Hl4JoshlTnMgdha3brXBRFXB+cWtYvJjjPhvmq3xqpbioi4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.0",
"rollup": "^4.9.5"
},
"peerDependencies": {
"@sveltejs/kit": "^2.4.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.15.1.tgz",
@ -1129,6 +1252,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
@ -1567,6 +1697,13 @@
"node": ">= 0.8"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -1940,6 +2077,13 @@
"node": ">=4.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@ -2139,6 +2283,16 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -2196,6 +2350,19 @@
"node": ">=8"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -2244,6 +2411,22 @@
"node": ">=0.8.19"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -2267,6 +2450,13 @@
"node": ">=0.10.0"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -2626,6 +2816,13 @@
"node": ">=8"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -2639,8 +2836,6 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
@ -2853,6 +3048,27 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -3044,6 +3260,19 @@
"node": ">=8"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svelte": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.16.0.tgz",

View File

@ -15,6 +15,7 @@
"devDependencies": {
"@eslint/compat": "^1.2.3",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"eslint": "^9.7.0",

View File

@ -0,0 +1,188 @@
/* Reset and base styles */
:root {
--primary-color: #4a90e2;
--secondary-color: #f5f5f5;
--border-color: #ddd;
--text-color: #333;
--error-color: #e74c3c;
--success-color: #2ecc71;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: #fff;
padding: 2rem;
}
/* Typography */
h1 {
font-size: 2rem;
margin-bottom: 1.5rem;
color: var(--text-color);
}
h2 {
font-size: 1.5rem;
margin: 1.5rem 0 1rem;
}
/* Links */
a {
color: var(--primary-color);
text-decoration: none;
transition: color 0.2s;
}
a:hover {
color: #357abd;
text-decoration: underline;
}
/* Lists */
ul {
list-style: none;
margin: 1rem 0;
}
li {
padding: 0.75rem;
border-bottom: 1px solid var(--border-color);
}
li:last-child {
border-bottom: none;
}
/* Forms */
form {
max-width: 800px;
margin: 2rem 0;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
input[type='text'],
input[type='number'],
input[type='date'],
select,
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 1rem;
margin-bottom: 1rem;
transition: border-color 0.2s;
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
textarea {
min-height: 100px;
resize: vertical;
}
/* Buttons */
button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s;
}
button:hover:not(:disabled) {
background-color: #357abd;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
button.secondary {
background-color: var(--secondary-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}
button.secondary:hover:not(:disabled) {
background-color: #e8e8e8;
}
button + button {
margin-left: 1rem;
}
/* Field management */
.field-container {
background-color: var(--secondary-color);
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
/* Submissions */
.submissions-list {
background-color: var(--secondary-color);
padding: 1rem;
border-radius: 4px;
}
.submission-item {
background-color: white;
padding: 1rem;
margin-bottom: 0.5rem;
border-radius: 4px;
box-shadow: var(--shadow);
}
/* Utility classes */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.loading {
text-align: center;
padding: 2rem;
color: #666;
}
.error {
color: var(--error-color);
margin: 1rem 0;
}
.success {
color: var(--success-color);
margin: 1rem 0;
}

View File

@ -0,0 +1,11 @@
<nav>
<h1>Formies</h1>
<button>logout</button>
</nav>
<style>
nav {
display: flex;
justify-content: space-between;
}
</style>

View File

@ -0,0 +1,7 @@
<script lang="ts">
import Navbar from '$lib/components/Navbar.svelte';
let { children } = $props();
</script>
<Navbar></Navbar>
{@render children()}

View File

@ -6,12 +6,10 @@
let fields: FormField[] = [];
function addField() {
// Use a new array assignment to trigger reactivity
fields = [...fields, { label: '', name: '', field_type: 'text' }];
}
function removeField(index: number) {
// Reassign to trigger reactivity
fields = fields.filter((_, i) => i !== index);
}
@ -27,36 +25,38 @@
}
</script>
<h1>Create Form</h1>
<div class="container">
<h1>Create Form</h1>
<form on:submit|preventDefault={saveForm}>
<div class="form-group">
<label>Form Name:</label>
<input type="text" bind:value={name} placeholder="Enter form name" />
</div>
<label>
Form Name:
<input type="text" bind:value={name} placeholder="Enter form name" />
</label>
<h2>Fields</h2>
{#each fields as field, i}
<div>
<label>
Label:
<input type="text" bind:value={field.label} placeholder="Enter field label" />
</label>
<label>
Name:
<input type="text" bind:value={field.name} placeholder="Enter field name" />
</label>
<label>
Type:
<select bind:value={field.field_type}>
<option value="text">Text</option>
<option value="number">Number</option>
<option value="date">Date</option>
<option value="textarea">Textarea</option>
</select>
</label>
<button on:click={() => removeField(i)}>Remove</button>
</div>
{/each}
<button on:click={addField}>Add Field</button>
<button on:click={saveForm} disabled={!name || fields.length === 0}> Save Form </button>
<h2>Fields</h2>
{#each fields as field, i}
<div class="field-container">
<div class="form-group">
<label>Label:</label>
<input type="text" bind:value={field.label} placeholder="Enter field label" />
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" bind:value={field.name} placeholder="Enter field name" />
</div>
<div class="form-group">
<label>Type:</label>
<select bind:value={field.field_type}>
<option value="text">Text</option>
<option value="number">Number</option>
<option value="date">Date</option>
<option value="textarea">Textarea</option>
</select>
</div>
<button class="secondary" on:click={() => removeField(i)}>Remove</button>
</div>
{/each}
<button class="secondary" on:click={addField}>Add Field</button>
<button type="submit" disabled={!name || fields.length === 0}>Save Form</button>
</form>
</div>

View File

@ -5,13 +5,12 @@
import { page } from '$app/stores';
export let params: { id: string };
console.log('params.id:', params);
let form: any | null = null;
let submissions: any[] = [];
let responseData: Record<string, any> = {};
onMount(async () => {
const { id } = $page.params; // Use $page.params to access route parameters
const { id } = $page.params;
if (id) {
form = await getForms().then((forms) => forms.find((f: any) => f.id === id) || null);
submissions = await getSubmissions(id);
@ -21,41 +20,42 @@
});
async function submitResponse() {
const { id } = $page.params; // Use $page.params to access route parameters
const { id } = $page.params;
await submitForm(id, responseData);
alert('Response submitted successfully!');
submissions = await getSubmissions(params.id); // Refresh submissions
submissions = await getSubmissions(params.id);
}
</script>
<h1>{form?.name}</h1>
{#if form}
<form on:submit|preventDefault={submitResponse}>
{#each form.fields as field}
<div>
<!-- svelte-ignore a11y_label_has_associated_control -->
<label>{field.label}</label>
{#if field.field_type === 'text'}
<input type="text" bind:value={responseData[field.name]} />
{:else if field.field_type === 'number'}
<input type="number" bind:value={responseData[field.name]} />
{:else if field.field_type === 'date'}
<input type="date" bind:value={responseData[field.name]} />
{:else if field.field_type === 'textarea'}
<textarea bind:value={responseData[field.name]}></textarea>
{/if}
</div>
{/each}
<button type="submit">Submit</button>
</form>
<h2>Submissions</h2>
<ul>
{#each submissions as submission}
<li>{JSON.stringify(submission.data)}</li>
{/each}
</ul>
{:else}
<p>Loading...</p>
{/if}
<div class="container">
<h1>{form?.name}</h1>
{#if form}
<form on:submit|preventDefault={submitResponse}>
{#each form.fields as field}
<div class="form-group">
<label>{field.label}</label>
{#if field.field_type === 'text'}
<input type="text" bind:value={responseData[field.name]} />
{:else if field.field_type === 'number'}
<input type="number" bind:value={responseData[field.name]} />
{:else if field.field_type === 'date'}
<input type="date" bind:value={responseData[field.name]} />
{:else if field.field_type === 'textarea'}
<textarea bind:value={responseData[field.name]}></textarea>
{/if}
</div>
{/each}
<button type="submit">Submit</button>
</form>
<h2>Submissions</h2>
<div class="submissions-list">
{#each submissions as submission}
<div class="submission-item">
{JSON.stringify(submission.data)}
</div>
{/each}
</div>
{:else}
<p class="loading">Loading...</p>
{/if}
</div>

View File

@ -11,80 +11,12 @@
</script>
<div class="container">
<h1>Formies</h1>
<a href="/create" class="create-button">Create a New Form</a>
{#if forms.length > 0}
<ul class="forms-list">
{#each forms as form}
<li class="form-item">
<a href={`/form/${form.id}`} class="form-link">
{form.name}
</a>
</li>
{/each}
</ul>
{:else}
<div class="empty-state">
<p>No forms created yet. Create your first form to get started!</p>
</div>
{/if}
<a href="/create" class="button">Create a New Form</a>
<ul class="forms-list">
{#each forms as form}
<li>
<a href={`/form/${form.id}`}>{form.name}</a>
</li>
{/each}
</ul>
</div>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
h1 {
color: #2c3e50;
margin-bottom: 2rem;
}
.create-button {
display: inline-block;
background-color: #42b983;
color: white;
padding: 0.8rem 1.5rem;
text-decoration: none;
border-radius: 4px;
margin-bottom: 2rem;
transition: background-color 0.2s;
}
.create-button:hover {
background-color: #3aa876;
}
.forms-list {
list-style: none;
padding: 0;
}
.form-item {
background-color: #f8f9fa;
margin-bottom: 1rem;
border-radius: 4px;
transition: transform 0.2s;
}
.form-item:hover {
transform: translateX(5px);
}
.form-link {
display: block;
padding: 1rem;
color: #2c3e50;
text-decoration: none;
border-left: 4px solid #42b983;
}
.empty-state {
text-align: center;
color: #666;
padding: 2rem;
}
</style>

View File

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */