diff --git a/.gitea/workflows/build_and_deploy.yml b/.gitea/workflows/build_and_deploy.yml
deleted file mode 100644
index aa40d8d..0000000
--- a/.gitea/workflows/build_and_deploy.yml
+++ /dev/null
@@ -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
diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml
new file mode 100644
index 0000000..2928f82
--- /dev/null
+++ b/.gitea/workflows/docker-build.yml
@@ -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
diff --git a/Dockerfile b/Dockerfile
index e6eeabc..f008b07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -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
diff --git a/backend/form_data.db b/backend/form_data.db
index a6446aa..1aa21ec 100644
Binary files a/backend/form_data.db and b/backend/form_data.db differ
diff --git a/backend/src/main.rs b/backend/src/main.rs
index 1a18a38..d2173bd 100644
--- a/backend/src/main.rs
+++ b/backend/src/main.rs
@@ -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",
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2a7900a..fed880a 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -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",
diff --git a/frontend/package.json b/frontend/package.json
index f44fb0f..b1ef6c7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -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",
diff --git a/frontend/src/app.css b/frontend/src/app.css
index e69de29..1435e65 100644
--- a/frontend/src/app.css
+++ b/frontend/src/app.css
@@ -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;
+}
diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte
new file mode 100644
index 0000000..7036c68
--- /dev/null
+++ b/frontend/src/lib/components/Navbar.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/frontend/src/routes/(auth)/+layout.svelte b/frontend/src/routes/(auth)/+layout.svelte
new file mode 100644
index 0000000..d6bb38e
--- /dev/null
+++ b/frontend/src/routes/(auth)/+layout.svelte
@@ -0,0 +1,7 @@
+
+
+
Loading...
-{/if} +Loading...
+ {/if} +No forms created yet. Create your first form to get started!
-