@jobsearch-works/firestore-models 1.1.15 β†’ 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # firestore-models
1
+ # Firestore Models
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@jobsearch-works/firestore-models.svg?style=flat-square)](https://www.npmjs.com/package/@jobsearch-works/firestore-models)
4
4
  [![license](https://img.shields.io/npm/l/@jobsearch-works/firestore-models.svg?style=flat-square)](./LICENSE)
5
5
 
6
+ Type-safe Firestore document models and path builders.
7
+
6
8
  A shared library for standardizing Firestore document schemas and paths across multiple projects.
7
9
 
8
10
  This library provides:
@@ -11,491 +13,202 @@ This library provides:
11
13
  - Pure Firestore path string builders (SDK-free)
12
14
  - Centralized schema definitions for use across apps
13
15
 
14
- ---
15
-
16
16
  ## πŸ“¦ Installation
17
17
 
18
18
  ```bash
19
19
  npm install @jobsearch-works/firestore-models
20
20
  ```
21
21
 
22
- For development:
23
-
24
- ```bash
25
- npm install github:jobsearch-works/firestore-models
26
- ```
27
-
28
- > ℹ️ Prefer the npm version for production. GitHub installs don't update via `npm update`.
29
-
30
- ---
31
-
32
- ## 🎯 Purpose
22
+ ## Single Source of Truth
33
23
 
34
- This package acts as a single source of truth for Firestore schema and paths, enabling:
24
+ This package is the single source of truth for:
35
25
 
36
- - Shared types across web apps, Node services, and Firebase functions
37
- - Consistent Firestore path generation (no hardcoded strings)
38
- - Cross-environment compatibility (e.g., CLI, testing, rules)
26
+ - Database paths (`@firestorePaths.ts`)
27
+ - Document types (`@types`)
28
+ - Static data (`@static`)
39
29
 
40
- This package provides no Firestore SDK bindings. Firestore `DocumentReference`s and `CollectionReference`s are constructed in your application using these path strings, typically in a `firestoreRefs.ts` file like:
30
+ Example usage:
41
31
 
42
- ```ts
43
- import { doc, collection } from "firebase/firestore";
44
- import { firestore } from "./firebaseConfig";
32
+ ```typescript
33
+ // Paths (from @firestorePaths.ts)
45
34
  import { firestorePaths } from "@jobsearch-works/firestore-models";
46
-
47
- export const userRef = (userId: string) =>
48
- doc(firestore, firestorePaths.users.doc(userId));
35
+ const path = firestorePaths.clients.jobPreferences(
36
+ "hovdKgHzIDS7c8Kzt13QNG0xsEN2"
37
+ );
38
+
39
+ // Types (from @types)
40
+ import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
41
+ const data: ClientJobPreferences = {
42
+ /* ... */
43
+ };
44
+
45
+ // Static data (from @static)
46
+ import { locations } from "@jobsearch-works/firestore-models/static/locations";
47
+ import jobClassifications from "@jobsearch-works/firestore-models/static/jobClassification";
49
48
  ```
50
49
 
51
- ---
50
+ ## Quick Start
52
51
 
53
- ## 🧱 Project Structure
54
-
55
- ```
56
- firestore-models/
57
- β”œβ”€β”€ src/
58
- β”‚ β”œβ”€β”€ types/ # Pure Firestore interfaces (SDK-free)
59
- β”‚ β”œβ”€β”€ paths/ # Pure path string builders (SDK-free)
60
- β”‚ └── index.ts # Main exports (types + paths only)
61
- β”œβ”€β”€ dist/ # Compiled output
62
- └── package.json
63
- ```
64
-
65
- ---
66
-
67
- ## πŸ”— Entity Relationships
68
-
69
- The following diagram and table illustrate how the main Firestore models relate to each other, both by reference (IDs) and by Firestore subcollections/paths:
52
+ ```typescript
53
+ import { firestorePaths } from "@jobsearch-works/firestore-models";
54
+ import { doc, getDoc } from "firebase/firestore";
55
+ import { firestore } from "./firebaseConfig";
70
56
 
57
+ // Get a client's job preferences
58
+ const clientId = "hovdKgHzIDS7c8Kzt13QNG0xsEN2"; // Real Firebase ID
59
+ const path = firestorePaths.clients.jobPreferences(clientId);
60
+ const ref = doc(firestore, path);
61
+ const snap = await getDoc(ref);
62
+ const data = { id: snap.id, ...snap.data() };
71
63
  ```
72
- Client
73
- β”œβ”€β”€ applications (Application)
74
- β”‚ └── questions (ApplicationQuestion)
75
- β”œβ”€β”€ questions (ClientQuestion)
76
- β”œβ”€β”€ preferences/targetPreferences (TargetPreferences)
77
- β”œβ”€β”€ vacancySuggestions (VacancySuggestion)
78
- β”œβ”€β”€ emails (GmailMessage)
79
- β”œβ”€β”€ logins (ClientLogin)
80
- └── resumeLinks (ResumeLink)
81
-
82
- Vacancy
83
- └── Referenced by Application and VacancySuggestion
84
-
85
- Agent
86
- └── Root collection
87
-
88
- User
89
- └── Root collection
90
-
91
- ClientData
92
- └── Root collection
93
- ```
94
-
95
- | Entity | References / Path | Description |
96
- | ------------------- | ---------------------------------------------------- | -------------------------------------------------------------------- |
97
- | Client | Root collection | Main user entity |
98
- | Application | clients/{clientId}/applications/{applicationId} | References `clientId`, `vacancyId` |
99
- | ApplicationQuestion | .../applications/{applicationId}/questions/{qId} | Subcollection of Application |
100
- | ClientQuestion | clients/{clientId}/questions/{questionId} | Subcollection of Client |
101
- | Vacancy | Root collection | Referenced by Application, VacancySuggestion |
102
- | VacancySuggestion | clients/{clientId}/vacancySuggestions/{suggestionId} | References `clientId`, `vacancyId` |
103
- | TargetPreferences | clients/{clientId}/preferences/targetPreferences | References `clientId`, contains `TargetJob[]` and `TargetLocation[]` |
104
- | TargetJob | Embedded in TargetPreferences | Contains `category` (VacancyCategory) |
105
- | TargetLocation | Embedded in TargetPreferences | List of preferred locations |
106
- | Agent | Root collection | Standalone entity |
107
- | AuthUser | Root collection | Standalone entity |
108
- | ClientData | Root collection | Standalone entity |
109
- | ClientLogin | clients/{clientId}/logins/{domain} | References `clientId` |
110
- | GmailMessage | clients/{clientId}/emails/{messageId} | References `clientId` |
111
- | ResumeLink | clients/{clientId}/resumeLinks/{linkId} | References `clientId`, stores PDF resume URLs |
112
64
 
113
- **Notes:**
65
+ ## Type Naming Convention
114
66
 
115
- - Most subcollections are nested under `Client` (e.g., applications, questions, vacancySuggestions).
116
- - `Application` and `VacancySuggestion` reference both `clientId` (parent) and `vacancyId` (target job).
117
- - `TargetPreferences` embeds arrays of `TargetJob` and `TargetLocation`.
118
- - All relationships are enforced by Firestore path structure and cross-referenced IDs.
67
+ Types follow their Firestore paths. For example:
119
68
 
120
- ---
69
+ - `clients/{id}/emailToken` β†’ `ClientEmailToken`
70
+ - `clients/{id}/jobPreferences` β†’ `ClientJobPreferences`
71
+ - `clients/{id}/personalData` β†’ `ClientPersonalData`
121
72
 
122
- ## βœ… Usage
73
+ This makes it easy to find the right type for any path.
123
74
 
124
- This library is intentionally SDK-free. All Firebase logic β€” including `Timestamp`, `doc()`, and `onSnapshot()` β€” is handled in your app's service layer. This ensures portability across React apps, Firebase functions, and testing environments.
75
+ ## Type Casting Results
125
76
 
126
- ### Importing Models and Paths
77
+ ```typescript
78
+ import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
79
+ import { doc, getDoc } from "firebase/firestore";
127
80
 
128
- ```ts
129
- import { Client } from "@jobsearch-works/firestore-models/types/Client";
130
- import { firestorePaths } from "@jobsearch-works/firestore-models/paths/firestorePaths";
81
+ // 1. Get the document
82
+ const path = firestorePaths.clients.jobPreferences(
83
+ "hovdKgHzIDS7c8Kzt13QNG0xsEN2"
84
+ );
85
+ const ref = doc(firestore, path);
86
+ const snap = await getDoc(ref);
131
87
 
132
- const clientDocPath = firestorePaths.clients.doc("abc123"); // β†’ "clients/abc123"
133
- ```
88
+ // 2. Cast the result (pick one method)
89
+ const data = { id: snap.id, ...snap.data() } as ClientJobPreferences;
134
90
 
135
- ---
136
-
137
- ## πŸ“„ Firestore Models (Types)
138
-
139
- All Firestore document schemas are defined as TypeScript interfaces in `src/types`. These are pure data types (SDK-free) and can be reused across apps, functions, and tests.
140
-
141
- > **Why does every model include `id?: string`?**
142
- > Firestore's `.data()` does **not** include the document ID. This library's types include `id` as an optional property, which is added manually when fetching data. This makes it easier to use models in UI, caching, and linking between entities.
143
-
144
- ### Main Types (alphabetical)
145
-
146
- - **Agent**
147
-
148
- ```ts
149
- export interface Agent {
150
- id?: string;
151
- name: string;
152
- email: string;
153
- status: "active" | "inactive";
154
- profilePicture?: string;
155
- createdAt?: Date | string;
156
- updatedAt?: Date | string;
157
- }
158
- ```
159
-
160
- - **Application**
161
-
162
- ```ts
163
- export interface Application {
164
- id?: string;
165
- vacancyId: string;
166
- clientId: string;
167
- status: ApplicationStatus;
168
- assignedTo?: string;
169
- coverLetter?: string;
170
- resume?: string;
171
- // Status timestamps
172
- submittedAt?: Date | string;
173
- interviewingAt?: Date | string;
174
- acceptedAt?: Date | string;
175
- rejectedAt?: Date | string;
176
- withdrawnAt?: Date | string;
177
- applyingAt?: Date | string;
178
- suggestedAt?: Date | string;
179
- approvedAt?: Date | string;
180
- // Denormalized/snapshot fields from Vacancy
181
- company?: string;
182
- position?: string;
183
- location?: string;
184
- jobId?: string;
185
- advertisingUrl?: string;
186
- applicationUrl?: string;
187
- applicationDomain?: string;
188
- advertisingDomain?: string;
189
- description?: string;
190
- fullPageText?: string;
191
- createdAt?: Date | string;
192
- updatedAt?: Date | string;
193
- }
194
- ```
195
-
196
- - `ApplicationStatus` values: new, submitted, interviewing, accepted, rejected, withdrawn, applying, suggested, approved
197
-
198
- - **ApplicationQuestion**
199
-
200
- ```ts
201
- export interface ApplicationQuestion {
202
- id?: string;
203
- questionText: string;
204
- answerText?: string;
205
- type: "text" | "select";
206
- createdAt?: Date | string;
207
- updatedAt?: Date | string;
208
- }
209
- ```
210
-
211
- - **AuthUser**
212
-
213
- ```ts
214
- export interface AuthUser {
215
- id?: string;
216
- email: string;
217
- displayName?: string;
218
- photoURL?: string;
219
- lastSignIn?: string; // ISO string
220
- emailVerified?: boolean;
221
- createdAt?: Date | string;
222
- updatedAt?: Date | string;
223
- }
224
- ```
225
-
226
- - **Client**
227
-
228
- ```ts
229
- export interface Client {
230
- id?: string;
231
- name: string;
232
- email: string;
233
- status: "active" | "inactive";
234
- resume?: string;
235
- createdAt?: Date | string;
236
- }
237
- ```
238
-
239
- - **ClientData**
240
-
241
- ```ts
242
- export interface ClientData {
243
- id?: string;
244
- firstName: string;
245
- lastName: string;
246
- middleName?: string;
247
- preferredName?: string;
248
- email: string;
249
- phone: string;
250
- address: string;
251
- city: string;
252
- suburb: string;
253
- state: string;
254
- zip: string;
255
- country: string;
256
- linkedIn?: string;
257
- countryPhoneCode: string;
258
- nationality: string;
259
- dateOfBirth?: Date | string;
260
- createdAt?: Date | string;
261
- updatedAt?: Date | string;
262
- }
263
- ```
264
-
265
- - **ClientLogin**
266
-
267
- ```ts
268
- export interface ClientLogin {
269
- id?: string;
270
- userId: string;
271
- url: string;
272
- domain: string;
273
- username?: string;
274
- password: string;
275
- email?: string;
276
- createdAt?: Date | string;
277
- updatedAt?: Date | string;
278
- }
279
- ```
280
-
281
- - **ClientQuestion**
282
-
283
- ```ts
284
- export interface ClientQuestion {
285
- id?: string;
286
- questionText: string;
287
- answerText?: string;
288
- type: "text" | "select";
289
- options?: string[];
290
- createdAt?: Date | string;
291
- updatedAt?: Date | string;
292
- }
293
- ```
294
-
295
- - **GmailMessage**
296
-
297
- ```ts
298
- export interface GmailMessage {
299
- id?: string;
300
- ```
301
-
302
- - **ResumeLink**
303
-
304
- ```ts
305
- export interface ResumeLink {
306
- id?: string;
307
- title: string;
308
- url: string;
309
- description: string;
310
- createdAt?: Date | string;
311
- createdBy: string;
312
- clientId: string;
313
- }
314
- ```
315
-
316
- - **GmailMessage**
317
-
318
- ```ts
319
- export interface GmailMessage {
320
- id?: string;
321
- messageId: string;
322
- threadId?: string;
323
- labelIds?: string[];
324
- snippet?: string;
325
- internalDate?: string;
326
- payload?: object;
327
- createdAt?: Date | string;
328
- updatedAt?: Date | string;
329
- }
330
- ```
331
-
332
- - **TargetJob**
333
-
334
- ```ts
335
- export interface TargetJob {
336
- id?: string;
337
- category: VacancyCategory;
338
- position: string;
339
- notes?: string;
340
- }
341
- ```
342
-
343
- - **TargetLocation**
344
-
345
- ```ts
346
- export interface TargetLocation {
347
- id?: string;
348
- country: string;
349
- cities: {
350
- name: string;
351
- areas: string[];
352
- }[];
353
- }
354
- ```
355
-
356
- - **TargetPreferences**
357
-
358
- ```ts
359
- export interface TargetPreferences {
360
- id?: string;
361
- clientId: string;
362
- locations: TargetLocation[];
363
- jobs: TargetJob[];
364
- createdAt?: Date | string;
365
- updatedAt?: Date | string;
366
- }
367
- ```
368
-
369
- - **Vacancy**
370
-
371
- ```ts
372
- export interface Vacancy {
373
- id?: string;
374
- company: string;
375
- position: string;
376
- location: string;
377
- description: string;
378
- advertisingUrl: string;
379
- applicationUrl: string;
380
- applicationDomain: string;
381
- advertisingDomain: string;
382
- fullPageText: string;
383
- jobId: string;
384
- category: VacancyCategory;
385
- suggestedTo?: string[];
386
- createdAt?: Date | string;
387
- updatedAt?: Date | string;
388
- }
389
- ```
390
-
391
- - `VacancyCategory` includes: Accounting, IT, Healthcare, Sales, etc.
392
-
393
- - **VacancySuggestion**
394
- ```ts
395
- export interface VacancySuggestion {
396
- id?: string;
397
- clientId: string;
398
- vacancyId: string;
399
- status: string;
400
- createdAt?: Date | string;
401
- }
402
- ```
403
-
404
- ---
405
-
406
- ## πŸ—‚οΈ Firestore Path Utilities
407
-
408
- All Firestore collection/document path builders are centralized in `src/paths/firestorePaths.ts` as pure string helpers. **Every helper returns a Firestore path string, not a Firestore SDK reference.** This keeps the library SDK-free and fully portable for use in any environment (Node.js, browser, SSR, CLI, etc). No Firestore SDK is required or included.
409
-
410
- ### Example Usage
411
-
412
- ```ts
413
- import { firestorePaths } from "@jobsearch-works/firestore-models/paths/firestorePaths";
414
-
415
- const clientDoc = firestorePaths.clients.doc("clientId"); // "clients/clientId"
416
- const appDoc = firestorePaths.clients.applications.doc(
417
- "clientId",
418
- "applicationId"
419
- ); // "clients/clientId/applications/applicationId"
420
- const vacancyDoc = firestorePaths.vacancies.doc("vacancyId"); // "vacancies/vacancyId"
91
+ // OR use the helper function
92
+ function withId<T>(snap: DocumentSnapshot): T & { id: string } {
93
+ return { id: snap.id, ...snap.data() } as T & { id: string };
94
+ }
95
+ const data = withId<ClientJobPreferences>(snap);
421
96
  ```
422
97
 
423
- #### Fetching a Firestore Document and Including Its ID
98
+ ## React + Zustand Example
424
99
 
425
- ```ts
426
- import { doc, getDoc } from "firebase/firestore";
427
- import { firestore } from "../firebaseConfig";
100
+ ```typescript
101
+ import { create } from "zustand";
102
+ import { doc, onSnapshot } from "firebase/firestore";
428
103
  import { firestorePaths } from "@jobsearch-works/firestore-models";
104
+ import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
105
+
106
+ // Store
107
+ interface JobPreferencesStore {
108
+ preferences: ClientJobPreferences | null;
109
+ loading: boolean;
110
+ error: Error | null;
111
+ fetchPreferences: (clientId: string) => void;
112
+ }
429
113
 
430
- const ref = doc(firestore, firestorePaths.vacancies.doc("vac123"));
431
- const snap = await getDoc(ref);
432
- const vacancy = { id: snap.id, ...snap.data() };
114
+ export const useJobPreferences = create<JobPreferencesStore>((set) => ({
115
+ preferences: null,
116
+ loading: false,
117
+ error: null,
118
+ fetchPreferences: (clientId: string) => {
119
+ set({ loading: true });
120
+ const path = firestorePaths.clients.jobPreferences(clientId);
121
+ const ref = doc(firestore, path);
122
+
123
+ return onSnapshot(
124
+ ref,
125
+ (snap) => {
126
+ const data = { id: snap.id, ...snap.data() } as ClientJobPreferences;
127
+ set({ preferences: data, loading: false });
128
+ },
129
+ (error) => set({ error, loading: false })
130
+ );
131
+ },
132
+ }));
133
+
134
+ // Component
135
+ function JobPreferences() {
136
+ const { preferences, loading, error, fetchPreferences } = useJobPreferences();
137
+ const clientId = "hovdKgHzIDS7c8Kzt13QNG0xsEN2";
138
+
139
+ useEffect(() => {
140
+ const unsubscribe = fetchPreferences(clientId);
141
+ return () => unsubscribe();
142
+ }, [clientId]);
143
+
144
+ if (loading) return <div>Loading...</div>;
145
+ if (error) return <div>Error: {error.message}</div>;
146
+ if (!preferences) return null;
147
+
148
+ return (
149
+ <div>
150
+ <h2>Job Preferences</h2>
151
+ <p>Desired Positions: {preferences.desiredPositions.join(", ")}</p>
152
+ <p>
153
+ Min Salary: {preferences.minSalary} {preferences.currency}
154
+ </p>
155
+ </div>
156
+ );
157
+ }
433
158
  ```
434
159
 
435
- **Reusable withId Utility:**
160
+ ## Path Structure
436
161
 
437
- ```ts
438
- export function withId<T>(snap: DocumentSnapshot): T & { id: string } {
439
- return { id: snap.id, ...snap.data() } as T & { id: string };
440
- }
162
+ ```typescript
163
+ // Collection paths
164
+ firestorePaths.clients.collection(); // "clients"
165
+ firestorePaths.clients.emails.collection("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2/emails"
166
+
167
+ // Document paths
168
+ firestorePaths.clients.doc("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2"
169
+ firestorePaths.clients.personalData("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2/personalData"
441
170
  ```
442
171
 
443
- #### Main Path Helpers
444
-
445
- - `firestorePaths.clients.collection()` β†’ "clients"
446
- - `firestorePaths.clients.doc(clientId)` β†’ "clients/{clientId}"
447
- - `firestorePaths.clients.applications.collection(clientId)` β†’ "clients/{clientId}/applications"
448
- - `firestorePaths.clients.applications.doc(clientId, applicationId)` β†’ "clients/{clientId}/applications/{applicationId}"
449
- - `firestorePaths.clients.applications.questions.collection(clientId, applicationId)` β†’ "clients/{clientId}/applications/{applicationId}/questions"
450
- - `firestorePaths.clients.applications.questions.doc(clientId, applicationId, questionId)` β†’ "clients/{clientId}/applications/{applicationId}/questions/{questionId}"
451
- - `firestorePaths.clients.emails.collection(clientId)` β†’ "clients/{clientId}/emails"
452
- - `firestorePaths.clients.emails.doc(clientId, messageId)` β†’ "clients/{clientId}/emails/{messageId}"
453
- - `firestorePaths.clients.logins.collection(clientId)` β†’ "clients/{clientId}/logins"
454
- - `firestorePaths.clients.logins.doc(clientId, domain)` β†’ "clients/{clientId}/logins/{domain}"
455
- - `firestorePaths.clients.preferences.target(clientId)` β†’ "clients/{clientId}/preferences/targetPreferences"
456
- - `firestorePaths.clients.questions.collection(clientId)` β†’ "clients/{clientId}/questions"
457
- - `firestorePaths.clients.questions.doc(clientId, questionId)` β†’ "clients/{clientId}/questions/{questionId}"
458
- - `firestorePaths.clients.resumeLinks.collection(clientId)` β†’ "clients/{clientId}/resumeLinks"
459
- - `firestorePaths.clients.resumeLinks.doc(clientId, resumeLinkId)` β†’ "clients/{clientId}/resumeLinks/{resumeLinkId}"
460
- - `firestorePaths.clients.vacancySuggestions.collection(clientId)` β†’ "clients/{clientId}/vacancySuggestions"
461
- - `firestorePaths.clients.vacancySuggestions.doc(clientId, suggestionId)` β†’ "clients/{clientId}/vacancySuggestions/{suggestionId}"
462
- - `firestorePaths.users.collection()` β†’ "users"
463
- - `firestorePaths.users.doc(userId)` β†’ "users/{userId}"
464
- - `firestorePaths.vacancies.collection()` β†’ "vacancies"
465
- - `firestorePaths.vacancies.doc(vacancyId)` β†’ "vacancies/{vacancyId}"
466
- - `firestorePaths.agents.collection()` β†’ "agents"
467
- - `firestorePaths.agents.doc(agentId)` β†’ "agents/{agentId}"
468
- - `firestorePaths.clientData.collection()` β†’ "clientData"
469
- - `firestorePaths.clientData.doc(clientDataId)` β†’ "clientData/{clientDataId}"
470
-
471
- ---
472
-
473
- ## πŸ›‘οΈ Notes
474
-
475
- - All types and paths are SDK-independent and safe to use in any TypeScript/Node/React project.
476
- - This package does not include any Firestore SDK or database logicβ€”just models and helpers.
477
- - For Firestore access, use your own service layer or SDK wrapper.
478
- - This library follows semantic versioning and is safe for production use.
479
-
480
- ---
481
-
482
- ## πŸ”— Path Builder Example
483
-
484
- ```ts
485
- // paths/pathStrings.ts
486
- export const pathStrings = {
487
- clients: () => "clients",
488
- client: (clientId: string) => `clients/${clientId}`,
489
- } as const;
172
+ ## Type Safety
173
+
174
+ ```typescript
175
+ import {
176
+ ClientJobPreferences,
177
+ JobCategory,
178
+ JobTitle,
179
+ LocationId,
180
+ } from "@jobsearch-works/firestore-models";
181
+
182
+ // Type-safe job preferences
183
+ const preferences: ClientJobPreferences = {
184
+ clientId: "hovdKgHzIDS7c8Kzt13QNG0xsEN2",
185
+ desiredPositions: ["Software Engineering"], // TypeScript autocomplete
186
+ categories: ["Information & Communication Technology"], // TypeScript autocomplete
187
+ locations: ["sydney", "london"], // TypeScript autocomplete
188
+ minSalary: 100000,
189
+ currency: "AUD",
190
+ industries: ["Technology"],
191
+ createdAt: new Date(),
192
+ updatedAt: new Date(),
193
+ };
490
194
  ```
491
195
 
492
- ---
196
+ ## Static Data
197
+
198
+ ```typescript
199
+ import jobClassifications from "@jobsearch-works/firestore-models/static/jobClassification";
200
+ import { locations } from "@jobsearch-works/firestore-models/static/locations";
493
201
 
494
- ## 🧩 Contributing
202
+ // Job categories and titles
203
+ type JobCategory = keyof typeof jobClassifications;
204
+ type JobTitle = (typeof jobClassifications)[JobCategory][number];
495
205
 
496
- When adding a new model:
206
+ // Location IDs
207
+ type LocationId = (typeof locations)[number]["id"];
208
+ ```
209
+
210
+ ## Installation
497
211
 
498
- 1. Add its interface under `src/types/`
499
- 2. Add its path builder in `src/paths/pathStrings.ts`
500
- 3. Update the documentation if needed
501
- 4. Maintain backward compatibility
212
+ ```bash
213
+ npm install @jobsearch-works/firestore-models
214
+ ```