| name | sikhaid-data |
| description | Use when working with data, state management, Firebase, or Firestore in SikhAid project. Covers Svelte stores, data flow patterns, Firebase initialization, Firestore CRUD operations, and data types. |
| allowed-tools | Read, Edit, Write |
SikhAid Data & State Management
State Management Architecture
Svelte Stores
Pattern: Reactive stores for client-side state management
Store Types Used:
writable()- Read/write stores for component state- No derived stores currently
- No custom stores (plain writable stores)
Store Locations:
src/lib/stores/
├── donation.ts # Donation amount state
├── contact.ts # Contact form submissions
├── volunteering.ts # Volunteer form submissions
└── csr.ts # CSR partnership submissions
Store Patterns
1. Donation Store
File: src/lib/stores/donation.ts
Purpose: Manage selected donation amount across components
import { writable } from 'svelte/store';
export const selectedAmount = writable<number>(0);
export function setDonationAmount(amount: number) {
selectedAmount.set(amount);
// Side effect: scroll to payment form
setTimeout(() => {
const paymentSection = document.getElementById('payment-form');
paymentSection?.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
Usage:
<script lang="ts">
import { selectedAmount, setDonationAmount } from '$lib/stores/donation';
// Read value with $ prefix
$: amount = $selectedAmount;
// Write value
function selectAmount(value: number) {
setDonationAmount(value);
}
</script>
<div>Selected: ₹{$selectedAmount}</div>
<button on:click={() => selectAmount(1000)}>₹1,000</button>
2. Contact Submissions Store
File: src/lib/stores/contact.ts
Purpose: Store contact form submissions (in-memory and Firestore)
import { writable } from 'svelte/store';
export interface ContactSubmission {
name: string;
email: string;
phone: string;
subject: string;
message: string;
timestamp: number;
}
export const contactSubmissions = writable<ContactSubmission[]>([]);
export function addContactSubmission(submission: ContactSubmission) {
contactSubmissions.update(submissions => {
return [...submissions, submission];
});
}
3. Volunteering Store
File: src/lib/stores/volunteering.ts
Purpose: Store volunteer applications
export interface VolunteerSubmission {
fullName: string;
email: string;
mobile: string;
gender: string;
address: string;
opportunity: string;
durationMonths: number;
durationYears: number;
startDate: string;
about: string;
timestamp: number;
}
export const volunteerSubmissions = writable<VolunteerSubmission[]>([]);
4. CSR Store
File: src/lib/stores/csr.ts
Purpose: Store CSR partnership inquiries
export interface CSRSubmission {
companyName: string;
contactPerson: string;
email: string;
phone: string;
companySize: string;
interests: string[];
message: string;
timestamp: number;
}
export const csrSubmissions = writable<CSRSubmission[]>([]);
Data Flow Pattern
Standard Form Submission Flow
1. User fills form
↓
2. Form validation
↓
3. Create submission object with timestamp
↓
4. Update Svelte store (addSubmission)
↓
5. Write to Firestore (addToFirestore)
↓
6. Show success/error feedback
↓
7. Reset form (optional)
Example Flow Implementation
<script lang="ts">
import { addContactSubmission } from '$lib/stores/contact';
import { addContactToFirestore } from '$lib/firestore';
let formData = { name: '', email: '', phone: '', subject: '', message: '' };
let isSubmitting = false;
let successMessage = '';
let errorMessage = '';
async function handleSubmit() {
// 1. Validate
if (!validateForm()) return;
isSubmitting = true;
errorMessage = '';
try {
// 2. Create submission object
const submission = {
...formData,
timestamp: Date.now()
};
// 3. Update store
addContactSubmission(submission);
// 4. Write to Firestore
await addContactToFirestore(submission);
// 5. Success feedback
successMessage = 'Submission received!';
formData = { name: '', email: '', phone: '', subject: '', message: '' };
} catch (error) {
// 6. Error feedback
errorMessage = 'Submission failed. Please try again.';
console.error('❌ Error:', error);
} finally {
isSubmitting = false;
}
}
</script>
Firebase Integration
Firebase Initialization
File: src/lib/firebase.ts
import { initializeApp, type FirebaseApp } from 'firebase/app';
import { getFirestore, type Firestore } from 'firebase/firestore';
import { getAuth, type Auth } from 'firebase/auth';
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID
};
let app: FirebaseApp | null = null;
let db: Firestore | null = null;
let auth: Auth | null = null;
// Browser-only initialization
if (typeof window !== 'undefined') {
try {
app = initializeApp(firebaseConfig);
db = getFirestore(app);
auth = getAuth(app);
console.log('✅ Firebase initialized successfully');
} catch (error) {
console.error('❌ Firebase initialization error:', error);
}
}
export { app, db, auth };
Key Points:
- Browser-only initialization (
typeof window !== 'undefined') - Environment variables for configuration
- Error handling with console logging
- Exports app, db, and auth instances
Firestore Operations
Collections Structure
Firestore Database
├── contact_submissions/
│ └── [auto-generated-id]/
│ ├── name: string
│ ├── email: string
│ ├── phone: string
│ ├── subject: string
│ ├── message: string
│ ├── status: 'new' | 'read' | 'resolved'
│ └── timestamp: Firestore Timestamp
│
├── volunteer_submissions/
│ └── [auto-generated-id]/
│ ├── fullName: string
│ ├── email: string
│ ├── mobile: string
│ ├── opportunity: string
│ ├── status: 'new' | 'read' | 'resolved'
│ └── ...
│
└── csr_submissions/
└── [auto-generated-id]/
├── companyName: string
├── email: string
├── status: 'new' | 'read' | 'resolved'
└── ...
Firestore Functions
File: src/lib/firestore.ts
Add Contact Submission
import { collection, addDoc, Timestamp } from 'firebase/firestore';
import { db } from './firebase';
import type { ContactSubmission } from './stores/contact';
export async function addContactToFirestore(
submission: ContactSubmission
): Promise<string> {
if (!db) throw new Error('Firestore not initialized');
try {
const docRef = await addDoc(collection(db, 'contact_submissions'), {
...submission,
status: 'new',
timestamp: Timestamp.fromMillis(submission.timestamp)
});
console.log('✅ Contact added to Firestore:', docRef.id);
return docRef.id;
} catch (error) {
console.error('❌ Error adding contact:', error);
throw error;
}
}
Get All Submissions
import { collection, getDocs, query, orderBy } from 'firebase/firestore';
export interface FirestoreContactSubmission extends ContactSubmission {
id?: string;
status: 'new' | 'read' | 'resolved';
firestoreTimestamp?: Timestamp;
}
export async function getContactSubmissions(): Promise<FirestoreContactSubmission[]> {
if (!db) throw new Error('Firestore not initialized');
try {
const q = query(
collection(db, 'contact_submissions'),
orderBy('timestamp', 'desc')
);
const querySnapshot = await getDocs(q);
const submissions: FirestoreContactSubmission[] = [];
querySnapshot.forEach((doc) => {
submissions.push({
id: doc.id,
...doc.data() as FirestoreContactSubmission
});
});
console.log(`📊 Fetched ${submissions.length} contact submissions`);
return submissions;
} catch (error) {
console.error('❌ Error fetching submissions:', error);
throw error;
}
}
Update Submission Status
import { doc, updateDoc } from 'firebase/firestore';
export async function updateSubmissionStatus(
collectionName: string,
documentId: string,
status: 'new' | 'read' | 'resolved'
): Promise<void> {
if (!db) throw new Error('Firestore not initialized');
try {
const docRef = doc(db, collectionName, documentId);
await updateDoc(docRef, { status });
console.log(`✅ Updated ${documentId} status to ${status}`);
} catch (error) {
console.error('❌ Error updating status:', error);
throw error;
}
}
Add Volunteer Submission
export async function addVolunteerToFirestore(
submission: VolunteerSubmission
): Promise<string> {
if (!db) throw new Error('Firestore not initialized');
try {
const docRef = await addDoc(collection(db, 'volunteer_submissions'), {
...submission,
status: 'new',
timestamp: Timestamp.fromMillis(submission.timestamp)
});
console.log('✅ Volunteer added:', docRef.id);
return docRef.id;
} catch (error) {
console.error('❌ Error adding volunteer:', error);
throw error;
}
}
Get Volunteer Submissions
export async function getVolunteerSubmissions(): Promise<FirestoreVolunteerSubmission[]> {
if (!db) throw new Error('Firestore not initialized');
try {
const q = query(
collection(db, 'volunteer_submissions'),
orderBy('timestamp', 'desc')
);
const querySnapshot = await getDocs(q);
const submissions: FirestoreVolunteerSubmission[] = [];
querySnapshot.forEach((doc) => {
submissions.push({
id: doc.id,
...doc.data() as FirestoreVolunteerSubmission
});
});
return submissions;
} catch (error) {
console.error('❌ Error fetching volunteers:', error);
throw error;
}
}
CSR Functions (Similar Pattern)
addCSRToFirestore()- Add CSR submissiongetCSRSubmissions()- Get all CSR submissions- Both follow same pattern as contact/volunteer
Static Data Management
Campaigns Data
File: src/lib/data/campaigns.js
Structure:
export const campaigns = [
{
slug: 'langar-aid',
title: 'Langar Aid',
subtitle: 'Free Meals for All',
shortDescription: 'Serving nutritious meals...',
fullDescription: 'Detailed description...',
image: '/images/campaign.jpg',
category: 'hunger-relief',
status: 'ongoing', // 'ongoing' | 'completed' | 'seasonal'
impactStats: [
{ label: 'Meals Served', value: '100,000+', icon: 'mdi:food' }
],
howItWorks: [
{ step: 1, title: 'Step Title', description: 'Step description' }
],
gallery: [
{ src: '/images/gallery1.jpg', alt: 'Description' }
]
},
// More campaigns...
];
Usage:
<script lang="ts">
import { campaigns } from '$lib/data/campaigns';
import type { Campaign } from '$lib/types/campaign';
// Filter campaigns
$: ongoingCampaigns = campaigns.filter(c => c.status === 'ongoing');
// Find by slug
$: campaign = campaigns.find(c => c.slug === 'langar-aid');
</script>
{#each ongoingCampaigns as campaign}
<div>{campaign.title}</div>
{/each}
Blog Data
File: src/lib/data/blogs.js
Structure:
export const blogs = [
{
slug: 'post-slug',
title: 'Blog Post Title',
excerpt: 'Short excerpt...',
content: 'Full blog content...',
author: 'Author Name',
date: '2024-01-15',
image: '/images/blog.jpg',
category: 'community'
},
// More blog posts...
];
TypeScript Types & Interfaces
Campaign Types
File: src/lib/types/campaign.ts
export interface Campaign {
slug: string;
title: string;
subtitle: string;
shortDescription: string;
fullDescription: string;
image: string;
category: string;
status: 'ongoing' | 'completed' | 'seasonal';
impactStats: ImpactStat[];
howItWorks: HowItWorksStep[];
gallery: GalleryImage[];
}
export interface ImpactStat {
label: string;
value: string;
icon: string;
}
export interface HowItWorksStep {
step: number;
title: string;
description: string;
}
export interface GalleryImage {
src: string;
alt: string;
}
Store Types (Defined in store files)
// Contact
export interface ContactSubmission {
name: string;
email: string;
phone: string;
subject: string;
message: string;
timestamp: number;
}
// Volunteer
export interface VolunteerSubmission {
fullName: string;
email: string;
mobile: string;
gender: string;
address: string;
opportunity: string;
durationMonths: number;
durationYears: number;
startDate: string;
about: string;
timestamp: number;
}
// CSR
export interface CSRSubmission {
companyName: string;
contactPerson: string;
email: string;
phone: string;
companySize: string;
interests: string[];
message: string;
timestamp: number;
}
Firestore Extended Types
import type { Timestamp } from 'firebase/firestore';
export interface FirestoreContactSubmission extends ContactSubmission {
id?: string;
status: 'new' | 'read' | 'resolved';
firestoreTimestamp?: Timestamp;
}
export interface FirestoreVolunteerSubmission extends VolunteerSubmission {
id?: string;
status: 'new' | 'read' | 'resolved';
firestoreTimestamp?: Timestamp;
}
export interface FirestoreCSRSubmission extends CSRSubmission {
id?: string;
status: 'new' | 'read' | 'resolved';
firestoreTimestamp?: Timestamp;
}
Data Loading in Components
Loading Firestore Data (Admin Panel)
<script lang="ts">
import { onMount } from 'svelte';
import {
getContactSubmissions,
type FirestoreContactSubmission
} from '$lib/firestore';
let submissions: FirestoreContactSubmission[] = [];
let isLoading = true;
let error = '';
onMount(async () => {
try {
submissions = await getContactSubmissions();
isLoading = false;
} catch (err) {
error = 'Failed to load submissions';
isLoading = false;
}
});
</script>
{#if isLoading}
<div>Loading...</div>
{:else if error}
<div class="text-red-500">{error}</div>
{:else}
{#each submissions as submission}
<div>{submission.name}</div>
{/each}
{/if}
Using Static Data
<script lang="ts">
import { campaigns } from '$lib/data/campaigns';
// No onMount needed - data is already available
$: featuredCampaigns = campaigns.filter(c => c.featured);
</script>
{#each featuredCampaigns as campaign}
<div>{campaign.title}</div>
{/each}
Store Subscription Patterns
Auto-subscribing with $ Prefix
<script lang="ts">
import { selectedAmount } from '$lib/stores/donation';
// Automatic subscription
$: amount = $selectedAmount;
</script>
<div>Amount: ₹{$selectedAmount}</div>
Manual Subscribe/Unsubscribe
<script lang="ts">
import { onDestroy } from 'svelte';
import { contactSubmissions } from '$lib/stores/contact';
let submissions = [];
const unsubscribe = contactSubmissions.subscribe(value => {
submissions = value;
});
onDestroy(() => {
unsubscribe();
});
</script>
Common Data Patterns
Filtering Arrays
<script lang="ts">
import { campaigns } from '$lib/data/campaigns';
let selectedCategory = 'all';
$: filteredCampaigns = selectedCategory === 'all'
? campaigns
: campaigns.filter(c => c.category === selectedCategory);
</script>
Searching
<script lang="ts">
let searchQuery = '';
let campaigns = [...];
$: searchResults = campaigns.filter(campaign =>
campaign.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
campaign.shortDescription.toLowerCase().includes(searchQuery.toLowerCase())
);
</script>
Sorting
<script lang="ts">
let submissions = [...];
let sortBy = 'timestamp'; // 'timestamp' | 'name' | 'status'
$: sortedSubmissions = [...submissions].sort((a, b) => {
if (sortBy === 'timestamp') {
return b.timestamp - a.timestamp; // Newest first
}
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
}
return 0;
});
</script>
When to Use This Skill
- Working with Svelte stores
- Integrating Firebase/Firestore
- Managing form submissions
- Loading or filtering data
- Creating new data types
- Implementing CRUD operations
- Understanding data flow in the app
Related Skills
sikhaid-forms- Form submission patternssikhaid-components- Component state managementsikhaid-overview- Firebase configuration