@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 +162 -449
- package/dist/index.d.mts +722 -151
- package/dist/index.d.ts +722 -151
- package/dist/index.js +47 -54
- package/dist/index.mjs +47 -53
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# Firestore Models
|
2
2
|
|
3
3
|
[](https://www.npmjs.com/package/@jobsearch-works/firestore-models)
|
4
4
|
[](./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
|
-
|
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
|
24
|
+
This package is the single source of truth for:
|
35
25
|
|
36
|
-
-
|
37
|
-
-
|
38
|
-
-
|
26
|
+
- Database paths (`@firestorePaths.ts`)
|
27
|
+
- Document types (`@types`)
|
28
|
+
- Static data (`@static`)
|
39
29
|
|
40
|
-
|
30
|
+
Example usage:
|
41
31
|
|
42
|
-
```
|
43
|
-
|
44
|
-
import { firestore } from "./firebaseConfig";
|
32
|
+
```typescript
|
33
|
+
// Paths (from @firestorePaths.ts)
|
45
34
|
import { firestorePaths } from "@jobsearch-works/firestore-models";
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
firestore
|
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
|
-
|
65
|
+
## Type Naming Convention
|
114
66
|
|
115
|
-
|
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
|
-
|
73
|
+
This makes it easy to find the right type for any path.
|
123
74
|
|
124
|
-
|
75
|
+
## Type Casting Results
|
125
76
|
|
126
|
-
|
77
|
+
```typescript
|
78
|
+
import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
|
79
|
+
import { doc, getDoc } from "firebase/firestore";
|
127
80
|
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
133
|
-
|
88
|
+
// 2. Cast the result (pick one method)
|
89
|
+
const data = { id: snap.id, ...snap.data() } as ClientJobPreferences;
|
134
90
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
98
|
+
## React + Zustand Example
|
424
99
|
|
425
|
-
```
|
426
|
-
import {
|
427
|
-
import {
|
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
|
431
|
-
|
432
|
-
|
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
|
-
|
160
|
+
## Path Structure
|
436
161
|
|
437
|
-
```
|
438
|
-
|
439
|
-
|
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
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
202
|
+
// Job categories and titles
|
203
|
+
type JobCategory = keyof typeof jobClassifications;
|
204
|
+
type JobTitle = (typeof jobClassifications)[JobCategory][number];
|
495
205
|
|
496
|
-
|
206
|
+
// Location IDs
|
207
|
+
type LocationId = (typeof locations)[number]["id"];
|
208
|
+
```
|
209
|
+
|
210
|
+
## Installation
|
497
211
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
4. Maintain backward compatibility
|
212
|
+
```bash
|
213
|
+
npm install @jobsearch-works/firestore-models
|
214
|
+
```
|