feat: Implement GraphQL API with optimized dashboard queries
Implemented complete GraphQL API with Apollo Server for efficient data fetching: Backend Changes: - Installed @nestjs/graphql@13.2.0, @nestjs/apollo@13.2.1, graphql@16.11.0, dataloader@2.2.3 - Configured Apollo Server with auto schema generation (src/schema.gql) - GraphQL Playground enabled in non-production environments - JWT authentication via GqlAuthGuard - Custom error formatting GraphQL Types (src/graphql/types/): - UserType with family relationships - ChildType with birthDate, gender, photoUrl - FamilyMemberType with role and user relation - ActivityGQLType with startedAt, endedAt, metadata - DashboardType aggregating all dashboard data - DailySummaryType with activity counts and totals - Enum types: ActivityType, FamilyRole, Gender, FeedingMethod, DiaperType Dashboard Resolver (src/graphql/resolvers/dashboard.resolver.ts): - Query: dashboard(childId?: ID) returns DashboardType - Single optimized query replacing 4+ REST API calls: * GET /api/v1/children * GET /api/v1/tracking/child/:id/recent * GET /api/v1/tracking/child/:id/summary/today * GET /api/v1/families/:id/members - Aggregates children, activities, family members, summaries in one query - ResolveField decorators for child and logger relations - Calculates daily summary (feeding, sleep, diaper, medication counts) - Uses Between for date range filtering - Handles metadata extraction for activity details DataLoader Implementation (src/graphql/dataloaders/): - ChildDataLoader: batchChildren, batchChildrenByFamily - UserDataLoader: batchUsers - REQUEST scope for per-request instance - Prevents N+1 query problem when resolving relations - Uses TypeORM In() for batch loading GraphQL Module (src/graphql/graphql.module.ts): - Exports ChildDataLoader and UserDataLoader - TypeORM integration with Child, Activity, FamilyMember, User entities - DashboardResolver provider Example Queries (src/graphql/example-queries.gql): - GetDashboard with childId parameter - GetDashboardAllChildren for listing - Documented usage and expected results Files Created (11 total): - src/graphql/types/ (5 files) - src/graphql/dataloaders/ (2 files) - src/graphql/resolvers/ (1 file) - src/graphql/guards/ (1 file) - src/graphql/graphql.module.ts - src/graphql/example-queries.gql Performance Improvements: - Dashboard load reduced from 4+ REST calls to 1 GraphQL query - DataLoader batching eliminates N+1 queries - Client can request only needed fields - Reduced network overhead and latency Usage: - Endpoint: http://localhost:3020/graphql - Playground: http://localhost:3020/graphql (dev only) - Authentication: JWT token in Authorization header 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
496
maternal-app/maternal-app-backend/package-lock.json
generated
496
maternal-app/maternal-app-backend/package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
||||
"@langchain/core": "^0.3.78",
|
||||
"@langchain/openai": "^0.6.14",
|
||||
"@nestjs/apollo": "^13.2.1",
|
||||
"@nestjs/common": "^11.1.6",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.1.6",
|
||||
@@ -39,6 +40,8 @@
|
||||
"cache-manager-redis-yet": "^5.1.5",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"dataloader": "^2.2.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"form-data": "^4.0.4",
|
||||
"graphql": "^16.11.0",
|
||||
@@ -66,7 +69,7 @@
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@nestjs/testing": "^11.1.6",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
@@ -119,6 +122,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
@@ -334,6 +361,22 @@
|
||||
"graphql": "14.x || 15.x || 16.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@apollo/server-plugin-landing-page-graphql-playground": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/server-plugin-landing-page-graphql-playground/-/server-plugin-landing-page-graphql-playground-4.0.1.tgz",
|
||||
"integrity": "sha512-tWhQzD7DtiTO/wfbGvasryz7eJSuEh9XJHgRTMZI7+Wu/omylG5gH6K6ksg1Vccg8/Xuglfi2f1M5Nm/IlBBGw==",
|
||||
"deprecated": "The use of GraphQL Playground in Apollo Server was supported in previous versions, but this is no longer the case as of December 31, 2022. This package exists for v4 migration purposes only. We do not intend to resolve security issues or other bugs with this package if they arise, so please migrate away from this to [Apollo Server's default Explorer](https://www.apollographql.com/docs/apollo-server/api/plugin/landing-pages) as soon as possible.",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apollographql/graphql-playground-html": "1.6.29"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@apollo/server": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apollo/server/node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
@@ -501,6 +544,15 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@apollographql/graphql-playground-html": {
|
||||
"version": "1.6.29",
|
||||
"resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz",
|
||||
"integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xss": "^1.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-crypto/crc32": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
|
||||
@@ -1996,7 +2048,7 @@
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
@@ -2009,7 +2061,7 @@
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
@@ -2141,23 +2193,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
@@ -2192,13 +2227,6 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -3378,7 +3406,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -3399,7 +3427,7 @@
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
@@ -3560,6 +3588,42 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/apollo": {
|
||||
"version": "13.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/apollo/-/apollo-13.2.1.tgz",
|
||||
"integrity": "sha512-BJPNw8xqs4DfdEEmjaAbI6cIJsHouWjcZN70BKTPl8rZcw4Tf61RonqFRn0F/rr/aiccWGAuXJuWY4dPsgah4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apollo/server-plugin-landing-page-graphql-playground": "4.0.1",
|
||||
"iterall": "1.3.0",
|
||||
"lodash.omit": "4.5.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@apollo/gateway": "^2.0.0",
|
||||
"@apollo/server": "^5.0.0",
|
||||
"@apollo/subgraph": "^2.0.0",
|
||||
"@as-integrations/fastify": "^2.1.1 || ^3.0.0",
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/graphql": "^13.0.0",
|
||||
"graphql": "^16.10.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@apollo/gateway": {
|
||||
"optional": true
|
||||
},
|
||||
"@apollo/subgraph": {
|
||||
"optional": true
|
||||
},
|
||||
"@as-integrations/express5": {
|
||||
"optional": true
|
||||
},
|
||||
"@as-integrations/fastify": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/cli": {
|
||||
"version": "10.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz",
|
||||
@@ -4091,9 +4155,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nestjs/testing": {
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.20.tgz",
|
||||
"integrity": "sha512-nMkRDukDKskdPruM6EsgMq7yJua+CPZM6I6FrLP8yXw8BiVSPv9Nm0CtcGGwt3kgZF9hfxKjGqLjsvVBsv6Vfw==",
|
||||
"version": "11.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.6.tgz",
|
||||
"integrity": "sha512-srYzzDNxGvVCe1j0SpTS9/ix75PKt6Sn6iMaH1rpJ6nj2g8vwNrhK0CoJJXvpCYgrnI+2WES2pprYnq8rAMYHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4104,10 +4168,10 @@
|
||||
"url": "https://opencollective.com/nest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/microservices": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.0"
|
||||
"@nestjs/common": "^11.0.0",
|
||||
"@nestjs/core": "^11.0.0",
|
||||
"@nestjs/microservices": "^11.0.0",
|
||||
"@nestjs/platform-express": "^11.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@nestjs/microservices": {
|
||||
@@ -5059,30 +5123,26 @@
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz",
|
||||
"integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz",
|
||||
"integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==",
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2"
|
||||
"cluster-key-slot": "1.1.2",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@redis/graph": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
||||
@@ -5092,42 +5152,6 @@
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/json": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz",
|
||||
"integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/search": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz",
|
||||
"integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/time-series": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz",
|
||||
"integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/node-cpu-profiler": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.2.0.tgz",
|
||||
@@ -6083,28 +6107,28 @@
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
@@ -7054,7 +7078,7 @@
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
@@ -7064,15 +7088,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
@@ -7098,6 +7122,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
@@ -7226,7 +7274,7 @@
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
@@ -7809,20 +7857,6 @@
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cache-manager-redis-yet/node_modules/@redis/client": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/cache-manager-redis-yet/node_modules/@redis/json": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
||||
@@ -7894,12 +7928,6 @@
|
||||
"@redis/time-series": "1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cache-manager-redis-yet/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/cache-manager/node_modules/keyv": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
|
||||
@@ -8435,7 +8463,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cron": {
|
||||
@@ -8483,6 +8511,28 @@
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssfilter": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dataloader": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
|
||||
"integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.18",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
||||
@@ -8643,7 +8693,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
@@ -9101,23 +9151,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
@@ -9165,13 +9198,6 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -9421,6 +9447,23 @@
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
|
||||
@@ -10056,7 +10099,7 @@
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5",
|
||||
@@ -10078,7 +10121,7 @@
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -11414,9 +11457,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -11863,6 +11906,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.omit": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
||||
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==",
|
||||
"deprecated": "This package is deprecated. Use destructuring assignment syntax instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
@@ -11976,7 +12026,7 @@
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/makeerror": {
|
||||
@@ -12213,7 +12263,7 @@
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
@@ -13472,6 +13522,66 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/@redis/bloom": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz",
|
||||
"integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/@redis/client": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz",
|
||||
"integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/@redis/json": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz",
|
||||
"integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/@redis/search": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz",
|
||||
"integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/@redis/time-series": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz",
|
||||
"integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
@@ -13739,30 +13849,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
@@ -14593,6 +14679,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
@@ -14621,6 +14724,13 @@
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||
@@ -14939,7 +15049,7 @@
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
@@ -15255,7 +15365,7 @@
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -15427,7 +15537,7 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-to-istanbul": {
|
||||
@@ -15669,7 +15779,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
@@ -15752,6 +15862,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xss": {
|
||||
"version": "1.0.15",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
|
||||
"integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^2.20.3",
|
||||
"cssfilter": "0.0.10"
|
||||
},
|
||||
"bin": {
|
||||
"xss": "bin/xss"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xss/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -15820,7 +15952,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
||||
"@langchain/core": "^0.3.78",
|
||||
"@langchain/openai": "^0.6.14",
|
||||
"@nestjs/apollo": "^13.2.1",
|
||||
"@nestjs/common": "^11.1.6",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.1.6",
|
||||
@@ -51,6 +52,8 @@
|
||||
"cache-manager-redis-yet": "^5.1.5",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"dataloader": "^2.2.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"form-data": "^4.0.4",
|
||||
"graphql": "^16.11.0",
|
||||
@@ -78,7 +81,7 @@
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@nestjs/testing": "^11.1.6",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { APP_GUARD, APP_FILTER } from '@nestjs/core';
|
||||
import { join } from 'path';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { DatabaseModule } from './database/database.module';
|
||||
@@ -17,6 +20,7 @@ import { AnalyticsModule } from './modules/analytics/analytics.module';
|
||||
import { FeedbackModule } from './modules/feedback/feedback.module';
|
||||
import { PhotosModule } from './modules/photos/photos.module';
|
||||
import { ComplianceModule } from './modules/compliance/compliance.module';
|
||||
import { GraphQLCustomModule } from './graphql/graphql.module';
|
||||
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
|
||||
import { ErrorTrackingService } from './common/services/error-tracking.service';
|
||||
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
|
||||
@@ -29,6 +33,28 @@ import { HealthController } from './common/controllers/health.controller';
|
||||
isGlobal: true,
|
||||
envFilePath: '.env',
|
||||
}),
|
||||
GraphQLModule.forRootAsync<ApolloDriverConfig>({
|
||||
driver: ApolloDriver,
|
||||
inject: [ConfigService],
|
||||
imports: [GraphQLCustomModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
||||
sortSchema: true,
|
||||
playground: configService.get('NODE_ENV') !== 'production',
|
||||
introspection: true,
|
||||
context: ({ req, res }, ...args) => {
|
||||
// DataLoaders will be provided via REQUEST scope
|
||||
return { req, res };
|
||||
},
|
||||
formatError: (error) => {
|
||||
return {
|
||||
message: error.message,
|
||||
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR',
|
||||
path: error.path,
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
ScheduleModule.forRoot(),
|
||||
DatabaseModule,
|
||||
CommonModule,
|
||||
@@ -43,6 +69,7 @@ import { HealthController } from './common/controllers/health.controller';
|
||||
FeedbackModule,
|
||||
PhotosModule,
|
||||
ComplianceModule,
|
||||
GraphQLCustomModule,
|
||||
],
|
||||
controllers: [AppController, HealthController],
|
||||
providers: [
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import * as DataLoader from 'dataloader';
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In } from 'typeorm';
|
||||
import { Child } from '../../database/entities/child.entity';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class ChildDataLoader {
|
||||
constructor(
|
||||
@InjectRepository(Child)
|
||||
private readonly childRepository: Repository<Child>,
|
||||
) {}
|
||||
|
||||
public readonly batchChildren = new DataLoader<string, Child>(
|
||||
async (childIds: readonly string[]) => {
|
||||
const children = await this.childRepository.find({
|
||||
where: { id: In([...childIds]) },
|
||||
});
|
||||
|
||||
const childMap = new Map(children.map((child) => [child.id, child]));
|
||||
return childIds.map((id) => childMap.get(id) || null);
|
||||
},
|
||||
);
|
||||
|
||||
public readonly batchChildrenByFamily = new DataLoader<
|
||||
string,
|
||||
Child[]
|
||||
>(async (familyIds: readonly string[]) => {
|
||||
const children = await this.childRepository.find({
|
||||
where: { familyId: In([...familyIds]) },
|
||||
});
|
||||
|
||||
const childrenByFamily = new Map<string, Child[]>();
|
||||
children.forEach((child) => {
|
||||
const existing = childrenByFamily.get(child.familyId) || [];
|
||||
existing.push(child);
|
||||
childrenByFamily.set(child.familyId, existing);
|
||||
});
|
||||
|
||||
return familyIds.map((familyId) => childrenByFamily.get(familyId) || []);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import * as DataLoader from 'dataloader';
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In } from 'typeorm';
|
||||
import { User } from '../../database/entities/user.entity';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UserDataLoader {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
public readonly batchUsers = new DataLoader<string, User>(
|
||||
async (userIds: readonly string[]) => {
|
||||
const users = await this.userRepository.find({
|
||||
where: { id: In([...userIds]) },
|
||||
});
|
||||
|
||||
const userMap = new Map(users.map((user) => [user.id, user]));
|
||||
return userIds.map((id) => userMap.get(id) || null);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
# Example GraphQL Queries for Maternal App
|
||||
|
||||
# ========================================
|
||||
# Dashboard Query - Optimized Single Query
|
||||
# ========================================
|
||||
# This query replaces multiple REST API calls:
|
||||
# - GET /api/v1/children
|
||||
# - GET /api/v1/tracking/child/:id/recent
|
||||
# - GET /api/v1/tracking/child/:id/summary/today
|
||||
# - GET /api/v1/families/:id/members
|
||||
|
||||
query GetDashboard($childId: ID) {
|
||||
dashboard(childId: $childId) {
|
||||
# Children list
|
||||
children {
|
||||
id
|
||||
name
|
||||
birthDate
|
||||
gender
|
||||
photoUrl
|
||||
}
|
||||
|
||||
# Selected child (specified or first)
|
||||
selectedChild {
|
||||
id
|
||||
name
|
||||
birthDate
|
||||
gender
|
||||
photoUrl
|
||||
}
|
||||
|
||||
# Recent activities (last 10 for selected child)
|
||||
recentActivities {
|
||||
id
|
||||
type
|
||||
startedAt
|
||||
endedAt
|
||||
notes
|
||||
metadata
|
||||
logger {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
# Today's summary for selected child
|
||||
todaySummary {
|
||||
date
|
||||
feedingCount
|
||||
totalFeedingAmount
|
||||
sleepCount
|
||||
totalSleepDuration
|
||||
diaperCount
|
||||
medicationCount
|
||||
}
|
||||
|
||||
# Family members
|
||||
familyMembers {
|
||||
userId
|
||||
role
|
||||
user {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
}
|
||||
|
||||
# Aggregations
|
||||
totalChildren
|
||||
totalActivitiesToday
|
||||
}
|
||||
}
|
||||
|
||||
# Example Variables:
|
||||
# {
|
||||
# "childId": "child_abc123"
|
||||
# }
|
||||
|
||||
# ========================================
|
||||
# Dashboard Query - All Children
|
||||
# ========================================
|
||||
# Get dashboard data without specifying a child
|
||||
# (will return first child's data)
|
||||
|
||||
query GetDashboardAllChildren {
|
||||
dashboard {
|
||||
children {
|
||||
id
|
||||
name
|
||||
birthDate
|
||||
}
|
||||
selectedChild {
|
||||
id
|
||||
name
|
||||
}
|
||||
totalChildren
|
||||
totalActivitiesToday
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Child } from '../database/entities/child.entity';
|
||||
import { Activity } from '../database/entities/activity.entity';
|
||||
import { FamilyMember } from '../database/entities/family-member.entity';
|
||||
import { User } from '../database/entities/user.entity';
|
||||
import { DashboardResolver } from './resolvers/dashboard.resolver';
|
||||
import { ChildDataLoader } from './dataloaders/child.dataloader';
|
||||
import { UserDataLoader } from './dataloaders/user.dataloader';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Child, Activity, FamilyMember, User])],
|
||||
providers: [DashboardResolver, ChildDataLoader, UserDataLoader],
|
||||
exports: [ChildDataLoader, UserDataLoader],
|
||||
})
|
||||
export class GraphQLCustomModule {}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Injectable, ExecutionContext } from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class GqlAuthGuard extends AuthGuard('jwt') {
|
||||
getRequest(context: ExecutionContext) {
|
||||
const ctx = GqlExecutionContext.create(context);
|
||||
return ctx.getContext().req;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { Resolver, Query, Args, Context, ResolveField, Parent } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Between } from 'typeorm';
|
||||
import { format } from 'date-fns';
|
||||
import { DashboardType, DailySummaryType } from '../types/dashboard.type';
|
||||
import { ActivityGQLType } from '../types/activity.type';
|
||||
import { ChildType } from '../types/child.type';
|
||||
import { UserType } from '../types/user.type';
|
||||
import { Child } from '../../database/entities/child.entity';
|
||||
import { Activity, ActivityType } from '../../database/entities/activity.entity';
|
||||
import { FamilyMember } from '../../database/entities/family-member.entity';
|
||||
import { User } from '../../database/entities/user.entity';
|
||||
import { GqlAuthGuard } from '../guards/gql-auth.guard';
|
||||
import { ChildDataLoader } from '../dataloaders/child.dataloader';
|
||||
import { UserDataLoader } from '../dataloaders/user.dataloader';
|
||||
|
||||
@Resolver(() => DashboardType)
|
||||
@UseGuards(GqlAuthGuard)
|
||||
export class DashboardResolver {
|
||||
constructor(
|
||||
@InjectRepository(Child)
|
||||
private readonly childRepository: Repository<Child>,
|
||||
@InjectRepository(Activity)
|
||||
private readonly activityRepository: Repository<Activity>,
|
||||
@InjectRepository(FamilyMember)
|
||||
private readonly familyMemberRepository: Repository<FamilyMember>,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
@Query(() => DashboardType, { name: 'dashboard' })
|
||||
async getDashboard(
|
||||
@Args('childId', { nullable: true }) childId: string,
|
||||
@Context() context: any,
|
||||
): Promise<DashboardType> {
|
||||
const userId = context.req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
|
||||
// Get user's family memberships
|
||||
const familyMemberships = await this.familyMemberRepository.find({
|
||||
where: { userId },
|
||||
relations: ['family'],
|
||||
});
|
||||
|
||||
const familyIds = familyMemberships.map((fm) => fm.familyId);
|
||||
|
||||
// Get all children in user's families
|
||||
const children = await this.childRepository.find({
|
||||
where: familyIds.length > 0 ? familyIds.map(id => ({ familyId: id })) : [],
|
||||
order: { createdAt: 'ASC' },
|
||||
});
|
||||
|
||||
// Select child (specified or first child)
|
||||
const selectedChild = childId
|
||||
? children.find((c) => c.id === childId)
|
||||
: children[0];
|
||||
|
||||
// Get today's date range
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
// Get recent activities (last 10 for selected child)
|
||||
const recentActivities = selectedChild
|
||||
? await this.activityRepository.find({
|
||||
where: { childId: selectedChild.id },
|
||||
order: { startedAt: 'DESC' },
|
||||
take: 10,
|
||||
})
|
||||
: [];
|
||||
|
||||
// Get today's activities for selected child
|
||||
const todayActivities = selectedChild
|
||||
? await this.activityRepository.find({
|
||||
where: {
|
||||
childId: selectedChild.id,
|
||||
startedAt: Between(today, tomorrow),
|
||||
},
|
||||
})
|
||||
: [];
|
||||
|
||||
// Calculate today's summary
|
||||
const todaySummary = this.calculateDailySummary(
|
||||
todayActivities,
|
||||
format(today, 'yyyy-MM-dd'),
|
||||
);
|
||||
|
||||
// Get all family members
|
||||
const familyMembers = await this.familyMemberRepository.find({
|
||||
where: familyIds.length > 0 ? familyIds.map(id => ({ familyId: id })) : [],
|
||||
relations: ['user'],
|
||||
});
|
||||
|
||||
return {
|
||||
children: children as any[],
|
||||
selectedChild: selectedChild as any,
|
||||
recentActivities: recentActivities as any[],
|
||||
todaySummary,
|
||||
familyMembers: familyMembers as any[],
|
||||
totalChildren: children.length,
|
||||
totalActivitiesToday: todayActivities.length,
|
||||
};
|
||||
}
|
||||
|
||||
@ResolveField(() => ChildType, { nullable: true })
|
||||
async child(
|
||||
@Parent() activity: ActivityGQLType,
|
||||
@Context() context: any,
|
||||
): Promise<ChildType> {
|
||||
const childLoader: ChildDataLoader = context.childLoader;
|
||||
return childLoader.batchChildren.load(activity.childId) as any;
|
||||
}
|
||||
|
||||
@ResolveField(() => UserType, { nullable: true })
|
||||
async logger(
|
||||
@Parent() activity: ActivityGQLType,
|
||||
@Context() context: any,
|
||||
): Promise<UserType> {
|
||||
const userLoader: UserDataLoader = context.userLoader;
|
||||
return userLoader.batchUsers.load(activity.loggedBy);
|
||||
}
|
||||
|
||||
private calculateDailySummary(
|
||||
activities: Activity[],
|
||||
date: string,
|
||||
): DailySummaryType {
|
||||
const summary = {
|
||||
feedingCount: 0,
|
||||
totalFeedingAmount: 0,
|
||||
sleepCount: 0,
|
||||
totalSleepDuration: 0,
|
||||
diaperCount: 0,
|
||||
medicationCount: 0,
|
||||
date,
|
||||
};
|
||||
|
||||
activities.forEach((activity) => {
|
||||
switch (activity.type) {
|
||||
case ActivityType.FEEDING:
|
||||
summary.feedingCount++;
|
||||
if (activity.metadata?.amount) {
|
||||
summary.totalFeedingAmount += activity.metadata.amount;
|
||||
}
|
||||
break;
|
||||
case ActivityType.SLEEP:
|
||||
summary.sleepCount++;
|
||||
if (activity.endedAt && activity.startedAt) {
|
||||
const duration = Math.floor(
|
||||
(activity.endedAt.getTime() - activity.startedAt.getTime()) / 60000,
|
||||
);
|
||||
summary.totalSleepDuration += duration;
|
||||
}
|
||||
break;
|
||||
case ActivityType.DIAPER:
|
||||
summary.diaperCount++;
|
||||
break;
|
||||
case ActivityType.MEDICATION:
|
||||
case ActivityType.MEDICINE:
|
||||
summary.medicationCount++;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { ObjectType, Field, ID, registerEnumType, Int, Float } from '@nestjs/graphql';
|
||||
import { ChildType } from './child.type';
|
||||
import { UserType } from './user.type';
|
||||
|
||||
export enum ActivityType {
|
||||
FEEDING = 'feeding',
|
||||
SLEEP = 'sleep',
|
||||
DIAPER = 'diaper',
|
||||
GROWTH = 'growth',
|
||||
MEDICATION = 'medication',
|
||||
MEDICINE = 'medicine',
|
||||
TEMPERATURE = 'temperature',
|
||||
MILESTONE = 'milestone',
|
||||
ACTIVITY = 'activity',
|
||||
}
|
||||
|
||||
export enum FeedingMethod {
|
||||
BREAST = 'breast',
|
||||
BOTTLE = 'bottle',
|
||||
SOLIDS = 'solids',
|
||||
}
|
||||
|
||||
export enum DiaperType {
|
||||
WET = 'wet',
|
||||
DIRTY = 'dirty',
|
||||
BOTH = 'both',
|
||||
}
|
||||
|
||||
registerEnumType(ActivityType, { name: 'ActivityType' });
|
||||
registerEnumType(FeedingMethod, { name: 'FeedingMethod' });
|
||||
registerEnumType(DiaperType, { name: 'DiaperType' });
|
||||
|
||||
@ObjectType('Activity')
|
||||
export class ActivityGQLType {
|
||||
@Field(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
childId: string;
|
||||
|
||||
@Field(() => ActivityType)
|
||||
type: ActivityType;
|
||||
|
||||
@Field()
|
||||
startedAt: Date;
|
||||
|
||||
@Field({ nullable: true })
|
||||
endedAt?: Date;
|
||||
|
||||
@Field()
|
||||
loggedBy: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
notes?: string;
|
||||
|
||||
// Metadata as JSON string or object
|
||||
@Field({ nullable: true })
|
||||
metadata?: string;
|
||||
|
||||
// Relations
|
||||
@Field(() => ChildType, { nullable: true })
|
||||
child?: ChildType;
|
||||
|
||||
@Field(() => UserType, { nullable: true })
|
||||
logger?: UserType;
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum Gender {
|
||||
MALE = 'male',
|
||||
FEMALE = 'female',
|
||||
OTHER = 'other',
|
||||
}
|
||||
|
||||
registerEnumType(Gender, {
|
||||
name: 'Gender',
|
||||
});
|
||||
|
||||
@ObjectType('Child')
|
||||
export class ChildType {
|
||||
@Field(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@Field()
|
||||
birthDate: Date;
|
||||
|
||||
@Field({ nullable: true })
|
||||
gender?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
photoUrl?: string;
|
||||
|
||||
@Field()
|
||||
familyId: string;
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { ObjectType, Field, Int } from '@nestjs/graphql';
|
||||
import { ChildType } from './child.type';
|
||||
import { ActivityGQLType } from './activity.type';
|
||||
import { FamilyMemberType } from './family.type';
|
||||
|
||||
@ObjectType('DailySummary')
|
||||
export class DailySummaryType {
|
||||
@Field(() => Int)
|
||||
feedingCount: number;
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
totalFeedingAmount?: number;
|
||||
|
||||
@Field(() => Int)
|
||||
sleepCount: number;
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
totalSleepDuration?: number;
|
||||
|
||||
@Field(() => Int)
|
||||
diaperCount: number;
|
||||
|
||||
@Field(() => Int)
|
||||
medicationCount: number;
|
||||
|
||||
@Field()
|
||||
date: string;
|
||||
}
|
||||
|
||||
@ObjectType('Dashboard')
|
||||
export class DashboardType {
|
||||
@Field(() => [ChildType])
|
||||
children: ChildType[];
|
||||
|
||||
@Field(() => ChildType, { nullable: true })
|
||||
selectedChild?: ChildType;
|
||||
|
||||
@Field(() => [ActivityGQLType])
|
||||
recentActivities: ActivityGQLType[];
|
||||
|
||||
@Field(() => DailySummaryType, { nullable: true })
|
||||
todaySummary?: DailySummaryType;
|
||||
|
||||
@Field(() => [FamilyMemberType])
|
||||
familyMembers: FamilyMemberType[];
|
||||
|
||||
@Field(() => Int)
|
||||
totalChildren: number;
|
||||
|
||||
@Field(() => Int)
|
||||
totalActivitiesToday: number;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql';
|
||||
import { UserType } from './user.type';
|
||||
|
||||
export enum FamilyRole {
|
||||
PARENT = 'parent',
|
||||
CAREGIVER = 'caregiver',
|
||||
}
|
||||
|
||||
registerEnumType(FamilyRole, {
|
||||
name: 'FamilyRole',
|
||||
});
|
||||
|
||||
@ObjectType('FamilyMember')
|
||||
export class FamilyMemberType {
|
||||
@Field()
|
||||
familyId: string;
|
||||
|
||||
@Field()
|
||||
userId: string;
|
||||
|
||||
@Field(() => FamilyRole)
|
||||
role: FamilyRole;
|
||||
|
||||
@Field(() => UserType, { nullable: true })
|
||||
user?: UserType;
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
import { FamilyMemberType } from './family.type';
|
||||
|
||||
@ObjectType('User')
|
||||
export class UserType {
|
||||
@Field(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
email: string;
|
||||
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
phone?: string;
|
||||
|
||||
@Field(() => [FamilyMemberType], { nullable: true })
|
||||
families?: FamilyMemberType[];
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
}
|
||||
Reference in New Issue
Block a user