form
This commit is contained in:
4
.env.example
Normal file
4
.env.example
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
VITE_APPWRITE_PROJECT_ID="696b82270034001dab69"
|
||||||
|
VITE_APPWRITE_ENDPOINT="https://appwrite.webklar.com/v1"
|
||||||
|
VITE_APPWRITE_DATABASE_ID="contacts"
|
||||||
|
VITE_APPWRITE_CONTACT_COLLECTION_ID="messages"
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
.env
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -39,6 +39,7 @@
|
|||||||
"@react-three/fiber": "^8.18.0",
|
"@react-three/fiber": "^8.18.0",
|
||||||
"@tabler/icons-react": "^3.36.1",
|
"@tabler/icons-react": "^3.36.1",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
|
"appwrite": "^22.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
@@ -3770,6 +3771,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/appwrite": {
|
||||||
|
"version": "22.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/appwrite/-/appwrite-22.0.0.tgz",
|
||||||
|
"integrity": "sha512-iFlfshYttuQheIyar6m789+Z/gvfKWQxWQCDhHzH9cEkFkn+laJZV8nMvGRH+1rTYNfAcFuycWKBGZiEDFxXug==",
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": "9.0.0",
|
||||||
|
"json-bigint": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@@ -3886,6 +3896,14 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/bignumber.js": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@@ -5742,6 +5760,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/json-bigint": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": "^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-buffer": {
|
"node_modules/json-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
"@react-three/fiber": "^8.18.0",
|
"@react-three/fiber": "^8.18.0",
|
||||||
"@tabler/icons-react": "^3.36.1",
|
"@tabler/icons-react": "^3.36.1",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
|
"appwrite": "^22.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
|
|||||||
42
src/lib/appwrite.ts
Normal file
42
src/lib/appwrite.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Appwrite-Anbindung für das Kontaktformular.
|
||||||
|
*
|
||||||
|
* In der Appwrite Console anlegen:
|
||||||
|
* 1. Database (z. B. ID: "contacts")
|
||||||
|
* 2. Collection (z. B. ID: "messages") mit String-Attributen: name, email, company, message
|
||||||
|
* 3. Unter "Settings" der Collection: Create-Berechtigung für "Any" aktivieren (öffentliches Formular)
|
||||||
|
* 4. IDs in .env setzen: VITE_APPWRITE_DATABASE_ID, VITE_APPWRITE_CONTACT_COLLECTION_ID
|
||||||
|
*/
|
||||||
|
import { Client, Databases, ID } from "appwrite";
|
||||||
|
|
||||||
|
const client = new Client()
|
||||||
|
.setEndpoint(import.meta.env.VITE_APPWRITE_ENDPOINT)
|
||||||
|
.setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID);
|
||||||
|
|
||||||
|
const databases = new Databases(client);
|
||||||
|
|
||||||
|
const CONTACT_DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID ?? "698124a20035e8f6dc42";
|
||||||
|
const CONTACT_COLLECTION_ID = import.meta.env.VITE_APPWRITE_CONTACT_COLLECTION_ID ?? "contact_submissions";
|
||||||
|
|
||||||
|
export type ContactFormData = {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
company: string;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createContactDocument(data: ContactFormData) {
|
||||||
|
return databases.createDocument<ContactFormData>(
|
||||||
|
CONTACT_DATABASE_ID,
|
||||||
|
CONTACT_COLLECTION_ID,
|
||||||
|
ID.unique(),
|
||||||
|
{
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
company: data.company,
|
||||||
|
message: data.message,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { client, databases };
|
||||||
@@ -6,6 +6,7 @@ import { Textarea } from "@/components/ui/textarea";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { ArrowLeft, Send } from "lucide-react";
|
import { ArrowLeft, Send } from "lucide-react";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { createContactDocument } from "@/lib/appwrite";
|
||||||
|
|
||||||
const Contact = () => {
|
const Contact = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -30,16 +31,23 @@ const Contact = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
// Simulate form submission
|
try {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await createContactDocument(formData);
|
||||||
|
toast({
|
||||||
toast({
|
title: "Nachricht gesendet!",
|
||||||
title: "Nachricht gesendet!",
|
description: "Wir melden uns innerhalb von 24 Stunden bei Ihnen.",
|
||||||
description: "Wir melden uns innerhalb von 24 Stunden bei Ihnen.",
|
});
|
||||||
});
|
setFormData({ name: "", email: "", company: "", message: "" });
|
||||||
|
} catch (err) {
|
||||||
setFormData({ name: "", email: "", company: "", message: "" });
|
const message = err instanceof Error ? err.message : "Speichern fehlgeschlagen.";
|
||||||
setIsSubmitting(false);
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Fehler beim Senden",
|
||||||
|
description: message,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
11
src/vite-env.d.ts
vendored
11
src/vite-env.d.ts
vendored
@@ -1 +1,12 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_APPWRITE_PROJECT_ID: string;
|
||||||
|
readonly VITE_APPWRITE_ENDPOINT: string;
|
||||||
|
readonly VITE_APPWRITE_DATABASE_ID?: string;
|
||||||
|
readonly VITE_APPWRITE_CONTACT_COLLECTION_ID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user