diff --git a/.gitea/workflows/android-build.yml b/.gitea/workflows/android-build.yml index 459c8ed..72cb31e 100644 --- a/.gitea/workflows/android-build.yml +++ b/.gitea/workflows/android-build.yml @@ -30,6 +30,11 @@ jobs: node-version: 22 cache: 'npm' + # 版本号一致性: package.json / tauri.conf.json / Cargo.toml / Cargo.lock 必须完全相等。 + # tauri.conf.json 的 version 就是 APK 的 versionName, 漏改会打出一个版本号错误的安装包。 + - name: Check version consistency + run: node scripts/ci/check-version.mjs + - name: Setup JDK 17 uses: https://gitea.com/actions/setup-java@v4 with: diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 06cef12..c40afb3 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -1,12 +1,21 @@ name: Docker Build and Push -# 仅 main 分支 push 触发, 其他分支不触发 -# 每次主分支提交: 构建前端 + 生成 OTA 升级包 (签名) → 打进 Docker 镜像 → 部署 +# 仅主分支 (master) push 触发部署; 其他分支一律不触发 (见 readme「分支规则」) +# 每次提交: 构建前端 + 生成 OTA 升级包 (签名) → 打进 Docker 镜像 → 自动升级数据库 → 部署 # OTA 随镜像发布: 容器内 OTA_PACKAGE_DIR=/app/ota-package 指向打包的 ota-package/ +# +# 分支策略 (2026-10 调整): +# - master = 主分支, 唯一部署出口, push 即发布生产 (容器 iboard, 宿主 3001) +# - dev-* = 开发集成分支, 只做 CI 校验 (构建/版本一致性), **不再自动部署**; +# 要上线必须先合并到 master 再推 master +# - tmp-* = 单个问题的临时分支, 合到 dev-* 后删除 +# - android-*= 只用于触发 APK 外壳构建, 不部署; 改动前须与 master 同步 +# +# 历史: 之前 dev-* 与 master 共用同一套生产环境 (推送即发布), 容易把半成品带上线, +# 现已取消 dev-* 的 push 触发。 on: push: branches: - - main - master jobs: @@ -17,6 +26,11 @@ jobs: # 其他 VITE_API_BASE 值。否则 OTA 包里的前端可能连错后端, 导致 # "OTA 更新后登录失败/进不去内部"。与 android-build.yml 保持一致。 VITE_API_BASE: http://bh.vps.honor3.com:3001 + # OTA bundle 要求的最低原生二进制版本。 + # 与 package.json 的应用版本是**两个概念**: 前端 OTA 不碰原生层, + # 现有 APK 仍是 0.1.0, 所以这里固定 0.1.0 (否则旧 APK 会被判定为不兼容而收不到更新)。 + # 重打 APK 并确认原生层无破坏后, 再提升此值。 + OTA_MIN_BINARY: '0.1.0' steps: - uses: https://gitea.com/actions/checkout@v4 @@ -25,6 +39,16 @@ jobs: with: node-version: 22 + - name: Check version consistency & branch + # 1) package.json / tauri.conf.json / Cargo.toml / Cargo.lock 四处版本必须完全相等且不带 -ota + # (tauri.conf.json 的 version 就是 APK 的 versionName) + # 2) 防止 OTA 版本号与分支错位: OTA 包的 version 直接取自 package.json, + # dev-0.2 分支上必须是 0.2.x, 否则线上 OTA 会报成上一个版本的号 + # (master 分支跳过分支前缀校验, 但四处一致性照样强制) + env: + GITHUB_REF_NAME: ${{ github.ref_name }} + run: node scripts/ci/check-version.mjs + - name: Install deps run: npm ci --no-audit --no-fund @@ -43,7 +67,7 @@ jobs: OTA_NOTES: ${{ github.sha }} run: | set -euo pipefail - node scripts/build-ota.mjs --out ota-package --notes ${OTA_NOTES} + node scripts/build-ota.mjs --out ota-package --notes ${OTA_NOTES} --min-binary ${OTA_MIN_BINARY} echo "===== latest.json =====" cat ota-package/latest.json echo "===== bundle sha256 =====" @@ -57,10 +81,16 @@ jobs: run: docker images - name: Apply DB migrations - # 仓库内的 prisma 迁移自动应用 (migrate deploy 幂等, 无新迁移时为 no-op) + # 数据库自动升级: 按 prisma/migrations/ 下的迁移顺序应用 (生产库由 master 部署时统一升级) + # migrate deploy 幂等, 无新迁移时为 no-op + # 位置关键: 在 "Remove old container" 之前 —— 建表/改表成功后才切换流量, + # 迁移失败则整个 workflow 中断, 不会用新镜像顶掉仍在运行的旧容器 env: DATABASE_URL: ${{ secrets.DATABASE_URL }} - run: npx prisma migrate deploy + run: | + set -euo pipefail + npx prisma migrate status || true + npx prisma migrate deploy - name: Remove old container run: docker rm -vf iboard || true diff --git a/.gitea/workflows/ota-build.yml b/.gitea/workflows/ota-build.yml index d293808..40538a0 100644 --- a/.gitea/workflows/ota-build.yml +++ b/.gitea/workflows/ota-build.yml @@ -21,10 +21,10 @@ on: type: string default: '' min_binary: - description: '最低 binary 版本 (留空用 src-tauri/tauri.conf.json 的 version)' + description: '最低 binary 版本 (默认 0.1.0 = 现有 APK 的原生版本, 与前端应用版本无关)' required: false type: string - default: '' + default: '0.1.0' jobs: build-ota: @@ -56,7 +56,7 @@ jobs: set -euo pipefail OUT="ota-package" ARGS="--out ${OUT}" - # inputs 留空时 → 不传 --sequence/--min-binary, 脚本默认 sequence=时间戳, min-binary=tauri.conf version + # sequence 留空 → 脚本默认用时间戳; min_binary 默认 0.1.0 (与前端版本解耦) if [ -n "${{ inputs.sequence }}" ]; then ARGS="${ARGS} --sequence ${{ inputs.sequence }}"; fi if [ -n "${OTA_NOTES}" ]; then ARGS="${ARGS} --notes ${OTA_NOTES}"; fi if [ -n "${{ inputs.min_binary }}" ]; then ARGS="${ARGS} --min-binary ${{ inputs.min_binary }}"; fi diff --git a/.gitignore b/.gitignore index 9edb0b1..9123757 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,49 @@ -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build -/out -/.next - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -# Editor directories and files -.idea -.vscode -*.swp -*.swo -*~ -dist/* - -# local env (contains API keys) -/.env -.reasonix/* -# OTA 升级: 产物 (运行时生成, 部署时单独管理) + 私钥 (绝不能提交) -ota-package/* -!ota-package/README.md +# dependencies +/node_modules +/.pnp +.pnp.js -# apizero 文件缓存 (运行时按需生成) -cache/apizero/ -# Dockerfile 持久卷挂载点 (跟 ota-package 同类) -.cache/ -# 米家凭证缓存 (含 token, 绝不能提交) +# testing +/coverage + +# production +/build +/out +/.next + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Editor directories and files +.idea +.vscode +*.swp +*.swo +*~ +dist/* + +# local env (contains API keys) +/.env +.reasonix/* +# OTA 升级: 产物 (运行时生成, 部署时单独管理) + 私钥 (绝不能提交) +ota-package/* +!ota-package/README.md + +# apizero 文件缓存 (运行时按需生成) +cache/apizero/ +# Dockerfile 持久卷挂载点 (跟 ota-package 同类) +.cache/ +# 米家凭证缓存 (含 token, 绝不能提交) server/mihome/.cache/ +# 工作区记忆/会话数据 (本地私有, 不入库) +.workbuddy/ diff --git a/docs/环境监控模块设计.md b/docs/V0.1/环境监控模块设计.md similarity index 100% rename from docs/环境监控模块设计.md rename to docs/V0.1/环境监控模块设计.md diff --git a/docs/V0.2/数据库设计文档.md b/docs/V0.2/数据库设计文档.md new file mode 100644 index 0000000..003a9d4 --- /dev/null +++ b/docs/V0.2/数据库设计文档.md @@ -0,0 +1,325 @@ +# iBoard V0.2 数据库设计文档 + +| 项 | 内容 | +| --- | --- | +| 版本 | V0.2 | +| 文档状态 | 已实现(与代码一致) | +| 数据库 | MariaDB / MySQL 8(Prisma `provider = "mysql"`) | +| 字符集 | `utf8mb4 / utf8mb4_unicode_ci` | +| 时区约定 | 应用层统一 `Asia/Shanghai`,库内 `DATETIME` 存 UTC | +| 相关文件 | `prisma/schema.prisma`、`prisma/migrations/20261022120000_add_checkin_checklist/migration.sql` | +| 关联文档 | `需求文档.md`、`设计文档.md`、`界面设计.html` | + +--- + +## 1. 概述 + +V0.2 新增「打卡 / 日历 / 清单」三个功能域,共引入 **5 张表**: + +| 表 | 作用 | 归属域 | +| --- | --- | --- | +| `Habit` | 打卡项(每天/每周/每月/每年 n 次的目标定义) | 打卡 | +| `CheckIn` | 打卡记录(一次打卡 = 一条) | 打卡 | +| `CheckList` | 检查清单(出差/回老家等场景的一次性清单) | 清单 | +| `CheckListItem` | 检查项(勾选即完成) | 清单 | +| `CheckListTemplate` | 清单模板(系统预置 + 用户自定义) | 清单 | + +日历视图**不新增表**:完全由 `Habit` + `CheckIn` 按月聚合得出(见 §7.3)。 + +### 1.1 设计原则 + +1. **记录为真,进度不落库。** 环形进度、完成率、streak 一律由 `CheckIn` 现场聚合,不在 `Habit` 上冗余 `doneCount` 字段——避免并发计数不一致、以及用户中途把「每周 3 次」改成「每周 5 次」后需要回填历史的问题。 +2. **日期以本地日历日字符串存储。** `CheckIn.date` 用 `VARCHAR(10)` 存 `"YYYY-MM-DD"`(Asia/Shanghai 下的日历日),而不是 `DATETIME`:范围查询走索引、字典序等于时间序、天然幂等边界,且彻底规避「UTC 存 + 本地读」导致的跨零点归属错误。 +3. **软删除优先。** 打卡项删除默认走 `archived` 归档(保留历史统计),只有显式 `?hard=1` 才物理删除。 +4. **一切按 `userId` 隔离。** 所有业务表都带 `userId`,`Habit`/`CheckIn`/`CheckList`/`CheckListTemplate` 外键 `ON DELETE CASCADE` 到 `User`。 +5. **迁移只增不改。** 新增需求一律新迁移文件,不修改已执行的迁移(部署走 `prisma migrate deploy`)。 + +--- + +## 2. 概念模型(ER) + +``` + ┌──────────┐ + │ User │ (既有表, 本次仅新增反向关系) + └────┬─────┘ + ┌───────────────┼────────────────────┬─────────────────┐ + │ │ │ │ + ┌────▼────┐ ┌────▼────┐ ┌─────▼─────┐ ┌──────▼──────┐ + │ Habit │1───*│ CheckIn │ │ CheckList │1─* │CheckListItem│ + └─────────┘ └─────────┘ └───────────┘ └─────────────┘ + 打卡项 打卡记录 场景清单 检查项 + (周期+目标n) (habitId,date,seq) (done, doneAt) + ▲ + │ 生成(复制条目, 不建外键) + ┌─────┴──────────┐ + │CheckListTemplate│ + │ (builtin/自定义) │ + └────────────────┘ +``` + +基数说明: + +| 关系 | 基数 | 级联 | +| --- | --- | --- | +| User → Habit | 1 : N | Cascade | +| User → CheckIn | 1 : N | Cascade | +| Habit → CheckIn | 1 : N | Cascade | +| User → CheckList | 1 : N | Cascade | +| CheckList → CheckListItem | 1 : N | Cascade | +| User → CheckListTemplate | 1 : N(可空,系统预置为空) | Cascade | +| CheckListTemplate → CheckList | 无外键,创建时**复制**条目内容 | — | + +--- + +## 3. 表结构 + +### 3.1 `Habit` — 打卡项 + +周期目标的**定义**表,不含任何计数。 + +| 字段 | 类型 | 空 | 默认 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | INT PK | 否 | AUTO_INCREMENT | 主键 | +| `userId` | INT FK | 否 | — | 所属用户 | +| `name` | VARCHAR(191) | 否 | — | 名称,接口校验 1~20 字 | +| `period` | VARCHAR(191) | 否 | `'day'` | `day` \| `week` \| `month` \| `year` | +| `target` | INT | 否 | `1` | 周期内目标次数 n(校验 ≥1) | +| `color` | VARCHAR(191) | 否 | `'#1677ff'` | 环形与标记主色(7 色预设之一) | +| `icon` | VARCHAR(191) | 否 | `''` | emoji / 图标名,**V0.2 预留不展示** | +| `note` | VARCHAR(191) | 否 | `''` | 备注 | +| `archived` | BOOLEAN | 否 | `false` | 归档后不出现在待办,历史保留 | +| `sortOrder` | INT | 否 | `0` | 排序权重(升序) | +| `createdAt` | DATETIME(3) | 否 | now() | 用于「创建之前的日期不计入月历期望」 | +| `updatedAt` | DATETIME(3) | 否 | 自动 | — | + +索引:`INDEX(userId, archived, sortOrder)` — 覆盖「取当前用户未归档打卡项」主查询。 + +### 3.2 `CheckIn` — 打卡记录 + +**一次打卡 = 一条记录**,同日多次用 `seq` 递增。 + +| 字段 | 类型 | 空 | 默认 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | INT PK | 否 | AUTO_INCREMENT | 主键 | +| `habitId` | INT FK | 否 | — | 所属打卡项 | +| `userId` | INT FK | 否 | — | 冗余用户(用于单用户全量区间查询,避免 join) | +| `date` | VARCHAR(10) | 否 | — | 本地日历日 `"YYYY-MM-DD"` | +| `seq` | INT | 否 | `1` | 当日第几次(1..n,超额继续累加) | +| `note` | VARCHAR(191) | 否 | `''` | 单次备注(接口截断 200 字) | +| `source` | VARCHAR(191) | 否 | `'user'` | `user` \| `backfill`(补打) | +| `createdAt` | DATETIME(3) | 否 | now() | 打卡时刻(UTC) | + +约束与索引: + +| 名称 | 类型 | 字段 | 作用 | +| --- | --- | --- | --- | +| `CheckIn_habitId_date_seq_key` | **UNIQUE** | `(habitId, date, seq)` | 并发重复提交的幂等防线(冲突 → P2002 → 409) | +| `CheckIn_userId_date_idx` | INDEX | `(userId, date)` | 月历按月区间扫全量记录 | +| `CheckIn_habitId_date_idx` | INDEX | `(habitId, date)` | 单打卡项周期区间聚合 | + +### 3.3 `CheckList` — 检查清单 + +| 字段 | 类型 | 空 | 默认 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | INT PK | 否 | AUTO_INCREMENT | 主键 | +| `userId` | INT FK | 否 | — | 所属用户 | +| `title` | VARCHAR(191) | 否 | — | 清单名,如「出差 · 杭州」 | +| `scene` | VARCHAR(191) | 否 | `''` | 场景:`出差` \| `回老家` \| `其他` | +| `note` | VARCHAR(191) | 否 | `''` | 备注 | +| `archived` | BOOLEAN | 否 | `false` | 归档 | +| `createdAt` / `updatedAt` | DATETIME(3) | 否 | — | 列表默认按 `updatedAt` 倒序 | + +索引:`INDEX(userId, archived)` — 对应「进行中 / 已归档」两个分段列表。 + +### 3.4 `CheckListItem` — 检查项 + +| 字段 | 类型 | 空 | 默认 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | INT PK | 否 | AUTO_INCREMENT | 主键 | +| `listId` | INT FK | 否 | — | 所属清单 | +| `title` | VARCHAR(191) | 否 | — | 条目名(校验 ≤40 字) | +| `note` | VARCHAR(191) | 否 | `''` | 条目备注 | +| `done` | BOOLEAN | 否 | `false` | 是否已完成 | +| `doneAt` | DATETIME(3) | **是** | NULL | 勾选时刻;取消勾选置回 NULL | +| `sortOrder` | INT | 否 | `0` | 排序(升序,同值按 id) | +| `createdAt` / `updatedAt` | DATETIME(3) | 否 | — | — | + +索引:`INDEX(listId, sortOrder)`。 + +### 3.5 `CheckListTemplate` — 清单模板 + +| 字段 | 类型 | 空 | 默认 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | INT PK | 否 | AUTO_INCREMENT | 主键 | +| `userId` | INT FK | **是** | NULL | NULL = 系统预置(`builtin=true`) | +| `name` | VARCHAR(191) | 否 | — | 模板名 | +| `scene` | VARCHAR(191) | 否 | `''` | 场景标签 | +| `items` | **JSON** | 否 | — | `[{ title, note }]`,创建清单时展开为 `CheckListItem` | +| `builtin` | BOOLEAN | 否 | `false` | 系统预置:只读、不可删(接口层限制) | +| `createdAt` / `updatedAt` | DATETIME(3) | 否 | — | — | + +索引:`INDEX(userId, builtin)` — 查询条件为 `builtin=true OR userId=me`。 + +系统预置数据(首次访问 `GET /api/checklists/templates` 时**幂等写入**): + +| 模板 | 条目数 | 条目 | +| --- | --- | --- | +| 出差 | 11 | 身份证/证件、手机充电器+充电宝、笔记本电脑+电源、耳机、换洗衣物、洗漱包、常用药、雨伞、车票/机票/酒店确认、工牌/名片、报销单据 | +| 回老家 | 12 | 身份证/证件、钥匙、手机充电器+充电宝、换洗衣物、洗漱包、常用药、给家人的礼物/伴手礼、长辈的药品/营养品、口罩、现金零钱、车票/加油/行程确认、家里门窗水电检查 | + +--- + +## 4. 外键与级联 + +| 子表.字段 | 父表.字段 | ON DELETE | ON UPDATE | +| --- | --- | --- | --- | +| `Habit.userId` | `User.id` | CASCADE | CASCADE | +| `CheckIn.habitId` | `Habit.id` | CASCADE | CASCADE | +| `CheckIn.userId` | `User.id` | CASCADE | CASCADE | +| `CheckList.userId` | `User.id` | CASCADE | CASCADE | +| `CheckListItem.listId` | `CheckList.id` | CASCADE | CASCADE | +| `CheckListTemplate.userId` | `User.id` | CASCADE | CASCADE | + +删除链路:删用户 → 级联清 Habit/CheckIn/CheckList/Template;删 Habit → 清其全部 CheckIn;删 CheckList → 清其全部 Item。业务层删打卡项默认只置 `archived=true`,物理删需 `?hard=1`(此时先 `deleteMany` 记录再删主表)。 + +--- + +## 5. 关键设计决策 + +### 5.1 为什么 `date` 用 `VARCHAR(10)` 而不是 `DATETIME` + +| 维度 | VARCHAR(10) 本地日历日 | DATETIME(UTC) | +| --- | --- | --- | +| 跨零点归属 | 明确:由应用层按 Asia/Shanghai 计算一次,落库即定 | 读时需要再转时区,易错 | +| 范围查询 | `date >= '2026-10-01' AND date <= '2026-10-31'` 直接走索引 | 需函数/边界换算,易失效 | +| 幂等边界 | `UNIQUE(habitId, date, seq)` 天然按「日」收敛 | 需按日截取 | +| 字典序 | 等于时间序,可安全字符串比较 | — | +| 存储 | 10 字节 | 5 字节(略优,但不足以抵消上述成本) | + +代价:跨时区展示需自行换算——本项目全链路 Asia/Shanghai,不成为问题。 + +### 5.2 为什么不冗余 `doneCount` / `streak` + +- **并发**:计数需事务 + 行锁,聚合查询天然一致。 +- **改配置**:用户把「每周 3 次」改成「每周 5 次」,冗余计数立刻失真,需要全量重算;现方案改 `target` 立即生效。 +- **撤销**:撤销即删一条记录,无需维护计数器。 +- **成本**:单个用户打卡项数量在 10~30 量级,月历查询一次拉取整月记录(≤ 数百行)在内存聚合,开销可接受。 + +### 5.3 `seq` 与幂等 + +同日多次打卡用 `seq = max(seq)+1`。唯一约束 `(habitId, date, seq)` 使并发重复提交必然撞 P2002,服务端转 409「该次打卡已记录」,前端幂等收敛,不会出现「点两下加两次」的体验事故。 + +### 5.4 归档 vs 物理删除 + +| 操作 | 语义 | 数据影响 | +| --- | --- | --- | +| `DELETE /api/checkin/habits/:id` | 归档(默认) | `archived=true`,记录全保留 | +| `DELETE /api/checkin/habits/:id?hard=1` | 物理删除 | 先删 CheckIn,再删 Habit | +| `DELETE /api/checklists/:id` | 物理删除(一次性清单,不留历史) | 先删 Item,再删 List | + +理由:打卡是长期行为数据,历史构成 streak 与完成率;清单是短期事务,用完即归档/删除。 + +### 5.5 模板用 JSON 而非明细表 + +模板条目结构固定(`title` + `note`)、只在「创建清单」时整体读取并展开、不需要按条目检索或统计 —— 用 `JSON` 列避免额外的 `TemplateItem` 表与 join。展开后落到 `CheckListItem`,之后模板改动不影响已生成清单。 + +--- + +## 6. 数据量估算 + +单用户规模假设:打卡项 20 个,平均每天打卡 25 次;清单每月 4 份,每份 12 条。 + +| 表 | 年增量(单用户) | 行大小(约) | 年占用(约) | +| --- | --- | --- | --- | +| `CheckIn` | 25 × 365 ≈ 9,100 | ~80 B | ~0.7 MB | +| `CheckListItem` | 4 × 12 × 12 = 576 | ~90 B | ~0.05 MB | +| `Habit` / `CheckList` / `CheckListTemplate` | < 100 | ~200 B | 忽略 | + +结论:**无需分区、无需归档策略**。10 用户 × 5 年 ≈ 45 万行 CheckIn,仍远低于单表瓶颈。若未来接入多租户或做年视图长周期统计,再考虑按月汇总表。 + +--- + +## 7. 典型查询 + +### 7.1 今日待办 + 周期进度 + +```sql +-- 步骤 1: 取未归档打卡项 (走 Habit_userId_archived_sortOrder_idx) +SELECT id, name, period, target, color FROM Habit +WHERE userId = ? AND archived = 0 ORDER BY sortOrder, id; + +-- 步骤 2: 取覆盖所有周期窗口的最小区间内的记录 (走 CheckIn_userId_date_idx) +SELECT habitId, date FROM CheckIn +WHERE userId = ? AND date >= ? AND date <= ?; -- 如 week: '2026-10-19' ~ '2026-10-25' +-- 步骤 3: 内存按 habitId/date 分组 -> 计算 done/percent/streak +``` + +### 7.2 打一次卡 + +```sql +SELECT MAX(seq) FROM CheckIn WHERE habitId = ? AND date = ?; -- -> seq +INSERT INTO CheckIn (habitId, userId, date, seq, note, source) VALUES (?, ?, ?, ?, ?, 'user'); +-- 冲突 -> P2002 -> 409 +``` + +撤销(撤销本周期最近一次): + +```sql +SELECT id FROM CheckIn WHERE habitId = ? AND date BETWEEN ? AND ? +ORDER BY date DESC, seq DESC LIMIT 1; +DELETE FROM CheckIn WHERE id = ?; +``` + +### 7.3 月历(日历视图数据来源) + +```sql +SELECT habitId, date FROM CheckIn +WHERE userId = ? AND date >= '2026-10-01' AND date <= '2026-10-31'; +``` + +按天聚合后与 `Habit.expectedPerDay` 比较得出日状态: + +| 状态 | 条件 | 展示 | +| --- | --- | --- | +| `full` | 全部打卡项当日次数 ≥ 日均期望 | 绿点 | +| `over` | 有任一打卡项当日次数 > 日均期望 | 紫点 | +| `partial` | 有完成但未全完成 | 黄点 | +| `empty` | 无记录或当天无打卡项 | 无点 | + +`Habit.createdAt` 早于该日的才计入期望,避免新建打卡项把历史日期标成「未完成」。 + +### 7.4 清单列表(带进度) + +```sql +SELECT l.*, i.* FROM CheckList l +LEFT JOIN CheckListItem i ON i.listId = l.id +WHERE l.userId = ? AND l.archived = ? +ORDER BY l.updatedAt DESC, i.sortOrder, i.id; +``` + +--- + +## 8. 迁移 + +| 项 | 内容 | +| --- | --- | +| 迁移目录 | `prisma/migrations/20261022120000_add_checkin_checklist/` | +| 内容 | 5 张 `CREATE TABLE` + 6 条 `ADD CONSTRAINT`(见 `migration.sql`) | +| 生成方式 | 手写 SQL(与 `prisma migrate diff` 产出格式一致),已通过 `prisma migrate deploy` 验证 | +| 部署命令 | `npx prisma migrate deploy && npx prisma generate` | +| 回滚 | 无自动回滚。应急:`DROP TABLE CheckIn, CheckListItem, CheckList, CheckListTemplate, Habit;`(注意外键顺序)并把 `_prisma_migrations` 中该记录删除 | + +>V0.2 全为新增表与新增字段(`User` 侧仅新增 Prisma 反向关系,不产生 DDL),对既有数据零影响。 + +--- + +## 9. V0.3 预留 + +| 方向 | 可能的表变更 | +| --- | --- | +| 打卡提醒 | `Habit` 增加 `remindAt TIME`、`remindDays` | +| 打卡统计快照 | 新增 `HabitDailyStat(userId, date, done, expected)` 汇总表,用于年视图/热力图加速 | +| 清单共享协作 | 新增 `CheckListShare(listId, userId, role)` | +| 模板市场 | `CheckListTemplate` 增加 `visibility`、`useCount` | + +以上均可通过「新增表 / 新增可空字段」完成,不影响 V0.2 数据结构。 diff --git a/docs/V0.2/界面设计.html b/docs/V0.2/界面设计.html new file mode 100644 index 0000000..1ace70a --- /dev/null +++ b/docs/V0.2/界面设计.html @@ -0,0 +1,1704 @@ + + + + + +iBoard V0.2 界面设计 · 打卡 / 日历 / 清单 + + + +
+ +
+

iBoard V0.2 · 界面设计

+

打卡 / 日历 / 清单 —— 移动端静态高保真原型(设计基线 420 × 820,适配 375~430px)

+
+ 版本 V0.2(0.2.0)分支 dev-0.2antd-mobile 风格 + 底部 4 Tab(打卡默认)时区 Asia/Shanghai文档日期 2026-10-22 · 修订 2026-10-23 +
+
+ + +

1 设计规范Design Tokens

+

沿用项目既有 :root 变量(src/styles/globals.css),本模块不引入新主题。

+
+
+

语义色

+
+
#1677ff主色 / 今日 / 链接
+
#00b578完成 / 进度条
+
#ffc53d部分完成 / 粒子
+
#a855f7超额完成
+
#ff8f1f连续天数 / 超额徽标
+
#ff3141危险操作
+
#f5f5f5页面底色
+
#f7fbf8已完成卡片底
+
+
+ 日历·全部完成 + 日历·部分完成 + 日历·超额 +
+
+
+

布局与圆角

+
+ 视口420 × 820(375~430 自适应) + NavBar 高45px + TabBar 高72px(含安全区) + 页面内边距左右 12px + 卡片圆角10px + 卡片间距10px 纵向 + 卡片阴影0 1px 2px rgba(0,0,0,.04) + 环形尺寸58 × 58,环宽 6px + 最小点按区≥ 40 × 40 +
+
+
+

字号层级

+
18 / 700日期标题「10月22日」
+
17 / 600NavBar 标题「打卡」
+
16 / 700环形中心数字 / 弹窗标题
+
15 / 600打卡项名 / 清单标题
+
13 / 400正文 / 日历日期数字
+
12 / 400次要信息 / 操作链接
+
11 / 400备注 / 周期范围 / 徽标
+
+
+ + +

2 主界面三个子视图(V0.2 现状;合并方案见 2.12 ②)

+ +
+ + +
+
+ +
+
+
+
10月22日 周四
+
已完成 2 / 4
+
+
+
+ 新建
+
+
+ +
+ +
+
+ + + + +
0/2
+
+
+
喝水
+
每天 2 次连续 12
+
今日
+
+
打卡
+
编辑归档
+
+ + +
+
+ + + + +
1/3
+
+
+
健身
+
每周 3 次连续 5
+
10-19 ~ 10-25
+
+
打卡
+
撤销编辑归档
+
+ + +
+
+ + + + +
+ + + +
+
+
+
读书
+
每月 12 次连续 3
+
10-01 ~ 10-31
+
+
再打一次
+
撤销编辑归档
+
+ + +
+
+ + + + +
+ + + +
+ +1 +
+
+
吃药
+
每天 1 次连续 30
+
今日超额 1
+
+
再打一次
+
撤销编辑归档
+
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.1 打卡视图 · 今天默认进入 +

头部日期 + 完成数;未完成卡片排在前面。环形中心未达标显示 done/target,达标显示对勾(每天 1 次打完即刻闭合);超额在右上角显示 +n。点环或「打卡」按钮均可打卡,卡片底部提供撤销 / 编辑 / 归档。

+
+
+ + +
+
+ +
+
+
打卡
日历
清单
+
+
+ ‹2026年10月› + 回到今天 +
+
+
昨天
今天
明天
+
+ +
+
一二三四五六日
+
+
28
+
29
+
30
+
1
+
2
+
3
+
4
+ +
5
+
6
+
7
+
8
+
9
+
10
+
11
+ +
12
+
13
+
14
+
15
+
16
+
17
+
18
+ +
19
+
20
+
21
+
22
+
23
+
24
+
25
+ +
26
+
27
+
28
+
29
+
30
+
31
+
1
+
+
+ +
本月完成率 82%
+
今天 · 2026-10-22
+ +
+ 喝水 + 每天 2 次 · 当日 2/2 + 已达标 +
+
+ 健身 + 每周 3 次 · 当日 0/1 + 打卡 +
+
+ 读书 + 每月 12 次 · 当日 1/1 + 已达标 +
+
+ 吃药 + 每天 1 次 · 当日 2/1 + 已达标 +
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.2 日历视图 · 今天选中 +

周一起始月历 + 「昨天 / 今天 / 明天」快捷分段;日期下方状态点:绿=全部完成、黄=部分完成、紫=超额、无点=未完成或未来。今天用主色数字并选中高亮,未来日期数字弱化。下方为该日明细,可直接打卡;底部展示本月完成率。

+
+
+ + +
+
+ +
+
+
打卡
日历
清单
+
+
+
进行中
已归档
模板
+
+ 新建
+
+ +
+
出差 · 杭州出差
+
+
8/12› +
+
+
身份证 / 证件×
+
手机充电器 + 充电宝×
+
笔记本电脑 + 电源×
+
耳机×
+
换洗衣物×
+
洗漱包×
+
常用药×
+
车票 / 机票 / 酒店确认×
+
雨伞×
+
工牌 / 名片×
+
报销单据×
+
现金零钱×
+ +
添加检查项
添加
+
+ 全选全不选复制 + 存为模板归档删除 +
+
+
+ +
+
回老家 · 国庆回老家
+
+
0/12› +
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.3 清单视图 · 展开态 +

卡片头部常显标题、场景标签、进度条与 8/12;点击展开逐条勾选(勾选后绿色圆底、文字加删除线并弱化)。底部可继续添加检查项,并提供全选 / 全不选 / 复制 / 存为模板 / 归档 / 删除。收起态只保留头部一行。

+
+
+ + +
+
+ +
+
+
+
10月22日 周四
+
已完成 2 / 4
+
+
+ 新建
+
+
+
+
+ + + +
0/2
+
+
喝水
每天 2 次
今日
+
打卡
+
编辑归档
+
+
+
+
+
+
新建打卡项
+
例如:喝水 / 健身 / 读书
+
+
每天
每周
每月
每年
+
+
+
−1+
+
+
+
+ + + + +
+
+
补充说明
+
保存
+
取消
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.4 新建 / 编辑打卡项(底部弹层) +

名称 ≤20 字;周期四选一(每天 / 每周 / 每月 / 每年);目标次数用 Stepper,最小 1;颜色 7 色预设;备注可选。编辑态标题变为「编辑打卡项」并回填当前值。

+
+
+ + +
+
+ +
+
+
10月22日 周四
已完成 3 / 4
+
+ 新建
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + +
+ + + + +
+ +
+
+
吃药
每天 1 次连续 30
今日
+
再打一次
+
撤销编辑归档
+
+
+
+ + + +
1/3
+
+
健身
每周 3 次
10-19 ~ 10-25
+
打卡
+
撤销编辑归档
+
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.5 达标瞬间 · 完成特效 +

环形闭合 + 对勾缩放入场(0.32s);同时 14 颗粒子沿圆周扩散(26~58px,交替主色与金色)+ 主色光环脉冲(0.7s),总时长 ≤900ms,pointer-events:none 不阻塞后续操作;并触发 18ms 轻震动。特效恒开(V0.2 设计变更:头部不再有开关)。

+
+
+ + +
+
+ +
+
打卡
日历
清单
+
‹2026年10月›回到今天
+
昨天
今天
明天
+
+
一二三四五六日
+
+
28
29
30
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
27
+
28
29
+
30
31
+
1
+
+
+
本月完成率 82%
+
明天 · 2026-10-23计划(不可打卡)
+
喝水每天 2 次 · 当日 0/2待办
+
健身每周 3 次 · 当日 0/1待办
+
读书每月 12 次 · 当日 0/1待办
+
吃药每天 1 次 · 当日 0/1待办
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.6 日历 · 明天(计划,只读) +

未来日期不允许打卡:标题右侧显示「计划(不可打卡)」徽标,条目右侧只显示「待办」文案而不渲染按钮,月历上未来日期数字弱化且不打点。昨天视图则相反——可补打最近 7 天内的漏打卡。

+
+
+ + +
+
+ +
+
打卡
日历
清单
+
+
进行中
已归档
模板
+
+ 新建
+
+
+
出差 · 杭州出差
+
12/12›
+
+
+ + + + + + + + +
+
+
+
回老家 · 国庆回老家
+
0/12›
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.7 清单全部完成(12/12) +

进度条 100%、计数变为 12/12、卡片底色转 #f7fbf8,并播放与打卡一致的完成特效。清单完成后用户通常选择「归档」或「复制」复用。

+
+
+ + +
+
+ +
+
+
10月22日 周四
已完成 0 / 0
+
+ 新建
+
+
+
○
+

还没有打卡项

+
创建第一个打卡项
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.8 空态 +

三个视图均具备加载中 / 空态 / 错误(带重试)三态。空态给出明确引导按钮。V0.2 设计变更:打卡页头部不再有「特效开 / 特效关」开关,完成特效与触感反馈恒开,没有任何关闭入口。

+
+
+ + +
+
+ +
+
+
+
报警3
新闻
+
油价
经济数据
策略研究
经济文章
+
+
›
+
+
+
+
央行开展 5000 亿元 MLF 操作,中标利率持平
+
本月 MLF 净投放 2000 亿元,市场流动性保持合理充裕…
+
妙想财经2026-10-22#1
+
+
+
9 月社零同比增长 3.2%,服务消费延续回暖
+
餐饮收入增速回升至 5.1%,可选消费分化明显…
+
妙想财经2026-10-21#2
+
+
+
美联储会议纪要偏鹰,美元指数小幅走强
+
点阵图显示年内或仍有一次降息空间,但分歧加大…
+
妙想财经2026-10-21#3
+
+
+
OPEC+ 维持减产节奏,国际油价日内涨逾 1%
+
布伦特原油重回 78 美元上方,化工链成本端承压…
+
妙想财经2026-10-20#4
+
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.9 宏观 · 原「首页」报警/新闻并入设计变更 +

「首页」Tab 取消,报警与新闻并入宏观。2026-10-23 调整:两者移到最左(报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章),默认选中「新闻」;共 6 个子 Tab 可横向滚动,当前项底部有 3px 主色指示条;报警数量以红色角标挂在子 Tab 上。tab 条右侧增加渐变遮罩 + 箭头,提示还有未展示的 tab(详见 2.12 ④)。新闻详情为 /macro/news/:id,底部「宏观」保持高亮。

+
+
+ + +
+
+
+
+
C
+
Cheney
+
qian.huang@sunyard.com
+
+
+
前端升级 (OTA)
+
当前版本v0.2.0 · 20261022.1530
+
运行资源内置
+
+
+
开发版本
+
按版本号判新:仅当服务端版本号更高时才提示更新
+
+
+
+
+
检查更新
+
立即升级
+
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.10 我的 · OTA「开发版本」关闭(默认)新功能 +

开关默认关闭 = 按版本号判新:取服务端 latest.json 的 version(如 0.2.0-ota),与当前运行版本按数字字段逐段比较,更高才提示更新。用于生产,避免旧分支被重建(sequence 变大但版本更小)把设备拉回旧版。开关值保存在设备端 localStorage.iboard_ota_dev_mode。

+
+
+ + +
+
+
+
+
C
+
Cheney
+
qian.huang@sunyard.com
+
+
+
前端升级 (OTA)
+
当前版本v0.2.0 · 20261022.1530
+
运行资源内置
+
+
+
开发版本
+
按 build sequence 判新:每次构建都算新版本,便于开发联调
+
+
+
+
新版本v0.2.0-ota (288 KB)
+
+
立即升级
+
回滚
+
+
+
+
+
+ 打卡
+
+ 宏观
+
+ 监控
+
+ 我
+
+
+
+ 2.11 我的 · OTA「开发版本」打开 +

开关打开 = 按 build sequence 判新:走 hotswap 插件的 checkUpdate(),只要服务端 sequence 比本地大就算新版本——每次 CI 构建 sequence 都会变(默认取时间戳),所以任何一次提交都能推到设备上,适合开发联调。切换开关会清空上一次的检查结果,避免上一种口径的结论误导。

+
+
+ +
+ + +

2.12 V0.2.1 界面调整头部统一 / 日历·打卡合并 / 模板分类 / 宏观 tab 溢出

+

本节为 2026-10-23 提出的调整方案。其中 ①头部统一 / ③模板分类 / ④宏观 tab 溢出 已按本节实现; + ②日历与打卡合并 为待确认草案,确认后再改代码。

+ +

① 四个主 Tab 头部统一

+ + + + + + +
页调整前调整后
打卡NavBar 45px,标题「打卡」17px 深色23px,标题「打卡」12px 主色加粗,字距 1px
宏观NavBar 45px,无标题(空白)23px + 标题「宏观」(同打卡规格)
监控NavBar 45px,无标题(空白)23px + 标题「监控」(同打卡规格)
我NavBar 45px 空白条整条去掉,蓝色头像区直接顶到状态栏下方
+

实现为独立组件 PageHeader,只被打卡 / 宏观 / 监控 引用;「我」页不引用,因此三者互不影响。 + 头部背景取页面底色 #f5f5f5(不再是卡片白),视觉上更轻。

+ +
+
+
+ +
+
+
10月23日 周五
已完成 2 / 10
+
+ 新建
+
+
+
+
+ + + +
1/2
+
+
喝水
每天 2 次连续 12
今日
+
打卡
+
撤销编辑归档
+
+
+
+ + + +
0/1
+
+
俯卧撑
每周 1 次
10-19 ~ 10-25
+
打卡
+
编辑归档
+
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ①-a 打卡(头部已缩至 23px)已实现 +

原 45px 白色 NavBar 换成 23px 底色条 + 主色标题,页面上移,首屏多出一张卡片的高度。

+
+
+ +
+
+ +
+
+
+
报警1
新闻
油价
+
经济数据
策略研究
经济文章
+
+
›
+
+
+
+
央行开展 5000 亿元 MLF 操作,中标利率持平
+
本月 MLF 净投放 2000 亿元,市场流动性保持合理充裕…
+
妙想财经2026-10-22#1
+
+
+
9 月社零同比增长 3.2%,服务消费延续回暖
+
餐饮收入增速回升至 5.1%,可选消费分化明显…
+
妙想财经2026-10-21#2
+
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ①-b + ④ 宏观:头部标题化 · 报警/新闻置最左 · 右侧溢出提示已实现 +

顺序:报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章,默认选中「新闻」(/macro 自动跳 /macro/news)。
+ 溢出提示:tab 条仍可向右滚动时,右边缘出现白色渐变遮罩 + 主色圆底箭头「›」(1.5s 轻微左右摆动,可点击向右翻页),滚到末尾自动消失;切换 tab 时自动把激活项滚到可视区中间。
+ antd-mobile Tabs 自带的右侧渐变遮罩只有 30px 且无箭头,用户难以察觉,故叠加此提示层。

+
+
+
+ +

② 打卡:日历 + 打卡合并(待确认草案)

+

把原来的「打卡」「日历」两个 tab 合成一个:上半部日历,下半部打卡卡片。取舍按下述规则:

+ + + + + + + + + + +
项规则
页面结构SwipeTabs 从 3 个减为 2 个:日历(含打卡) / 清单
上方日历保留月历网格 + 月份左右切换 + 「回到今天」;去掉「昨天 / 今天 / 明天」分段控件(点日期即可,节省 38px 高度)
日历高度日期格子 38px → 31px,去掉 6 行补位中的空行;整块约 240px
下方打卡沿用现有打卡卡片样式,但紧凑化:内边距 12px → 8px,环形 58px → 42px(环宽 6 → 5),行高压缩,卡片间距 10 → 7px
卡片内容保留环形进度(含达标对勾 / 超额 +n)、名称、周期与目标、连续天数、打卡按钮;撤销 / 编辑 / 归档收进「⋯」,减少一行高度
分组按周期类型分组:每天 → 每周 → 每月,每组一个小标题(左侧主色竖条 + 组名 + 条数);去掉「每年」分组(已有年目标项并入「每月」区末尾的「其他周期」小标题,或建议直接隐藏年视图)
联动点日历某日 → 下方卡片即该日数据(进度仍按该日所属周期算);未来日期只读,按钮变灰显示「计划」
空态日历下方直接显示「该日没有打卡项」+「+ 新建」引导
+ +
+
+
+ +
+
+
日历
清单
+
+ +
+
+ ‹2026年10月› + 回到今天 +
+
一二三四五六日
+
+
29
30
+
1
2
+
3
4
5
+
6
7
+
8
9
+
10
11
12
+
13
14
15
+
16
17
18
19
+
20
21
+
22
23
24
25
+
26
27
28
+
29
30
31
1
+
+
本月完成率 82%今天 · 2026-10-22
+
+ +
今天已完成 2 / 4点环即打卡
+ +
+
每天2 项
+ +
+
+ + + + +
1/2
+
+
+
喝水
+
每天 2 次 · 今日 1/2连续 12
+
+
打卡
+
⋯
+
+ +
+
+ + + + +
+ +1 +
+
+
吃药
+
每天 1 次 · 今日 2/1连续 30
+
+
再打一次
+
⋯
+
+ +
每周1 项
+
+
+ + + + +
1/3
+
+
+
健身
+
每周 3 次 · 本周 1/3连续 5
+
+
打卡
+
⋯
+
+ +
每月1 项
+
+
+ + + + +
0/12
+
+
+
读书
+
每月 12 次 · 本月 0/12
+
+
打卡
+
⋯
+
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ②-a 合并后的主视图(日历在上 · 打卡在下,按天 / 周 / 月分组)待确认 +

卡片整体压缩:环形 42px、内边距 8px、行距 7px,单卡高度约 58px(原 92px),一屏可看 4~5 项。分组顺序固定为 每天 → 每周 → 每月, + 组标题左侧 3px 主色竖条;无「每年」分组。日历顶部的「昨天 / 今天 / 明天」分段已去掉,直接点日期切换,选中日靠「今天」主色数字与蓝色实心块识别。

+
+
+ +
+
+ +
+
日历
清单
+
+
‹2026年10月›回到今天
+
一二三四五六日
+
+
29
30
+
1
2
+
3
4
5
+
6
7
+
8
9
+
10
11
12
+
13
14
15
+
16
17
18
19
+
20
21
+
22
23
24
25
+
26
27
28
+
29
30
31
1
+
+
本月完成率 82%明天 · 2026-10-23
+
+
明天计划(不可打卡)
+
+
每天2 项
+
+
+ + + +
0/2
+
+
喝水
每天 2 次 · 当日 0/2
+
计划
⋯
+
+
+
+ + + +
0/1
+
+
吃药
每天 1 次 · 当日 0/1
+
计划
⋯
+
+
每周1 项
+
+
+ + + +
1/3
+
+
健身
每周 3 次 · 本周 1/3
+
计划
⋯
+
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ②-b 未来日期(计划态) +

选中未来日期时,打卡按钮统一变灰显示「计划」,点击不生效;环形仍显示该日所属周期的既有进度(此处举例为周期初)。 + 补打窗口仍是最近 7 天,超出时同样只读。

+
+
+ +
+
+ +
+
日历
清单
+
+
‹2026年10月›回到今天
+
一二三四五六日
+
+
29
30
+
1
2
+
3
4
5
+
6
7
+
8
9
+
10
11
12
+
13
14
15
+
16
17
18
19
+
20
21
+
22
23
24
25
+
26
27
28
+
29
30
31
1
+
+
本月完成率 82%今天 · 2026-10-22
+
+
今天已完成 2 / 4点环即打卡
+
+
每天2 项
+
+
+ + + +
1/2
+
+
喝水
每天 2 次 · 今日 1/2
+
打卡
⋯
+
+
+
+ + + + +
+ +1 +
+
吃药
每天 1 次 · 今日 2/1
+
再打一次
⋯
+
+
+
+
+
+
吃药 · 每天 1 次
+
撤销上次打卡今日 2 次
+
编辑打卡项名称 / 周期 / 目标
+
归档历史记录保留
+
取消
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ②-c 卡片「⋯」操作面板 +

紧凑化后卡片不再常显三个文字链接,改由右上角「⋯」唤出底部面板:撤销 / 编辑 / 归档。面板项 44px 高, + 「归档」用危险色;撤销仅在当日有记录时可用,否则置灰。

+
+
+
+ +

③ 清单:新增「模板」分类(与进行中 / 已归档并列)已实现

+

清单页分段从 2 个变为 3 个:进行中 / 已归档 / 模板。模板拥有独立的增删改查,按名称字母顺序(中文按拼音、数字按数值)排序; + 新建清单时只保留名称,去掉场景,模板改为点击后在右侧滑出的面板里选择。

+ +
+
+
+ +
+
日历
清单
+
+
进行中
已归档
模板
+
+ 新建模板
+
+ +
+
出差预置11 项
+
+ 身份证 / 证件手机充电器 + 充电宝 + 笔记本电脑 + 电源耳机+7 +
+
由此新建
+
+ +
+
回老家预置12 项
+
+ 身份证 / 证件钥匙 + 手机充电器 + 充电宝换洗衣物+8 +
+
由此新建
+
+ +
+
露营装备2 项
+
帐篷睡袋
+
由此新建编辑删除
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ③-a 模板分类已实现 +

模板卡片:名称 + 「预置」标记 + 项数;下方最多展示 4 个检查项摘要(超出折叠为 +n);底部操作「由此新建 / 编辑 / 删除」。 + 系统预置模板不可编辑、不可删除(只保留「由此新建」)。右上角按钮随分段切换:模板页为「+ 新建模板」,其余为「+ 新建」。

+
+
+ +
+
+ +
+
日历
清单
+
+
+
新建检查清单
+
周末露营
+
+ +
露营装备2 项 ›
+
+
创建
+
取消
+
+
+
选择模板×
+
按名称字母顺序排列
+
不使用模板空白清单
+
出差预置11 项
+
回老家预置12 项
+
露营装备2 项
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ③-b 新建清单 + 模板右侧滑出面板已实现 +

表单只剩两项:名称(≤20 字,必填)与 模板(可空)。场景选择器已移除——场景不再由用户填写,新建时沿用所选模板自带的场景(无模板则为空),历史数据仍正常展示场景标签。
+ 点模板行从右侧滑出 78% 宽面板:首项固定为「不使用模板」,其后按字母顺序列出全部模板(预置带标记、右侧显示项数),选中项主色高亮,点遮罩或 × 关闭。

+
+
+ +
+
+ +
+
日历
清单
+
+
+
编辑模板
+
露营装备
+
+ +
+
1
帐篷
×
+
2
睡袋
×
+
3
检查项内容
×
+
+
+ 添加检查项
+
+
保存
+
取消
+
+
+
+
打卡
+
宏观
+
监控
+
我
+
+
+
+ ③-c 模板编辑(增删改)已实现 +

新建 / 编辑共用同一弹层:模板名称(≤20 字)+ 检查项列表(逐条 ≤40 字,可删除、可追加)。保存时至少需 1 条有效检查项,名称为空或全空则拦截并 Toast 提示。 + 删除模板需二次确认,且不影响已用该模板创建过的清单。

+
+
+
+ +

④ 补充:模板接口

+ + + + + + + +
方法路径说明
GET/api/checklists/templates模板列表(含预置),服务端按名称字母顺序排序
GET/api/checklists/templates/:id模板详情
POST/api/checklists/templates新建模板 { name, items[] }
PUT/api/checklists/templates/:id改名称 / 检查项(预置模板拒绝)
DELETE/api/checklists/templates/:id删自建模板(预置模板拒绝)
+

排序口径:Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' }),中文按拼音、数字按数值比较,前后端一致。

+ + +

3 组件状态定义Component States

+ +

3.0 页面头部(PageHeader)

+ + + + + + + +
项取值
高度23px(原 NavBar 45px 的一半),flex: 0 0 23px 不参与压缩
标题字号 / 字重12px / 600
标题颜色主色 #1677ff
背景页面底色 #f5f5f5
适用范围打卡 / 宏观 / 监控;「我」页不使用
+

实现为独立组件 src/components/PageHeader.jsx,三页各自引用、互不影响;「我」页移除 NavBar 后头像区直接贴顶。

+ +

3.1 环形进度(尺寸 58,环宽 6,r=26,周长 163.4)

+ + + + + + + +
状态条件环形中心卡片底按钮
未开始done = 0仅灰底环0/2白打卡(主色)
进行中0 < done < target按 done/target 填充(45°…330°)1/3白打卡(主色)
已达标done = target闭合整圈对勾(主色)#f7fbf8再打一次(灰)
超额done > target闭合 + 右上角 +n 橙标对勾#f7fbf8再打一次(灰)
每日 1 次达标target = 1 且已打卡直接整圈闭合,不做渐增生长期对勾#f7fbf8再打一次
+

环形进度 = 周期完成度(done / n);日历日标记 = 当日份额(当日次数 / 日均期望),两者口径不同,互不影响。

+ +

3.2 日历日期

+ + + + + + + + + +
状态判定视觉
全部完成 full所有打卡项当日次数 ≥ 日均期望,且无超额绿色圆点
超额 over全部达标且存在任一打卡项当日次数 > 期望紫色圆点
部分完成 partial有完成但未全部完成黄色圆点
无 empty无记录 / 当天无打卡项 / 未来日期不显示圆点
今天date = today数字主色加粗
选中用户点击主色实心底,数字与圆点转白
非本月 / 未来月份不符 / date > today数字弱化(#c8c8c8 / #b0b0b0)
+ +

3.3 清单检查项

+ + + + + +
状态勾选框文字触发
未完成1.5px 灰边圆圈,对勾透明#333点击圆圈或文字切换
已完成绿底绿边,白色对勾显现(0.2s)#a0a0a0 + 删除线同上,写 doneAt
整单完成—进度条 100%、卡片转绿最后一项勾选时播放特效
+ + +

4 交互与动效Motion

+ + + + + + + + + +
动效时长曲线 / 实现说明
环形增长450mscubic-bezier(.4,0,.2,1),stroke-dashoffset 过渡12 点方向顺时针增长
对勾入场320msscale(.4) → 1 + 透明度达标瞬间出现
完成粒子≤900ms14 颗粒子按角度 --a、距离 --d 扩散,交错 40ms 延迟不阻塞点击
光环脉冲700ms主色圆环放大淡出与粒子同时开始
触感反馈18msnavigator.vibrate(18)WebView 不支持则静默跳过
进度条填充400mswidth 过渡清单勾选后更新
视图切换—SwipeTabs 跟手横滑与既有模块一致
+ +

4.1 打卡时序

+
用户点环 / 点「打卡」 + └─ 前端乐观更新:done+1,重算 percent / completed / over,环形立即增长 + └─ POST /api/checkin/habits/:id/check + ├─ 200 → 用服务端 item 覆盖本地;若 completed → 触发特效 + 震动 + ├─ 400 → 回滚 + Toast(未来日期 / 超出 7 天补打窗口) + ├─ 409 → 视为已打卡,刷新一次(幂等) + └─ 网络失败 → 回滚 + Toast「打卡失败」
+ + +

5 适配与可达性

+ + + + + + + +
项约定
视口设计稿 420×820;375~430px 内元素不换行、不裁切;底部 TabBar 4 项每项 ≥ 44px 可点宽度
安全区TabBar 使用 antd-mobile safeArea,页面底部留出 tabbar-h + safe-bottom
动效特效恒开(V0.2 设计变更,无开关):单次 ≤900ms、pointer-events:none,不阻塞连续打卡;不再响应 prefers-reduced-motion
大字号卡片采用 grid 自适应列宽,文字超长换行不截断;名称限制 20 字、检查项 40 字
深色模式V0.2 沿用现有浅色主题,暂不适配
+ +

本文档为静态设计稿,不含脚本逻辑;实现对应 src/pages/checkin/*.jsx、src/components/RingProgress.jsx 与样式段(src/styles/globals.css)。接口与数据口径见《设计文档.md》与《数据库设计文档.md》。

+ +
+ + diff --git a/docs/V0.2/设计文档.md b/docs/V0.2/设计文档.md new file mode 100644 index 0000000..a32a869 --- /dev/null +++ b/docs/V0.2/设计文档.md @@ -0,0 +1,516 @@ +# iBoard V0.2 设计文档 — 打卡 / 日历 / 清单 + +| 项 | 内容 | +| --- | --- | +| 版本 | V0.2(对应分支 `dev-0.2`,应用版本 `0.2.0`) | +| 文档状态 | 已实现(与代码一致;含 2026-10-22 设计变更,见 §10) | +| 上游文档 | `需求文档.md`(需求编号 F-CK / F-CL / F-LS / F-SY / U / D / N / E 沿用本文) | +| 配套文档 | `数据库设计文档.md`、`界面设计.html` | +| 技术栈 | Tauri 2 + React 18 + antd-mobile + React Router v6 + Express 4 + Prisma/MariaDB | +| 设计基线 | 移动端优先,视口 420×820,375~430px 自适应;时区统一 `Asia/Shanghai` | + +--- + +## 1. 总体设计 + +### 1.1 目标与范围 + +在既有 iBoard 之上**新增「打卡」模块并升为默认首页**(底部 Tab 由 5 个精简为 4 个:打卡 / 宏观 / 监控 / 我),页面内以三个子视图承载三类需求: + +| 子视图 | 解决问题 | 核心隐喻 | +| --- | --- | --- | +| **打卡** | 周期性习惯的量化坚持 | 环形进度,点环即打卡 | +| **日历** | 回看昨天、执行今天、预知明天 | 月历网格 + 日状态点 | +| **清单** | 出差/回老家等一次性场景的漏项检查 | 勾选清单 + 进度条 | + +> **Tab 结构(V0.2 终态)** +> +> | 顺序 | Tab | 路由 | 说明 | +> | --- | --- | --- | --- | +> | 1 | **打卡(默认)** | `/checkin` | 根路径 `/` 重定向到此;`/checkin` 内含 打卡/日历/清单 三视图 | +> | 2 | 宏观 | `/macro/*` | 油价 / 经济数据 / 策略研究 / 经济文章 / **报警** / **新闻**(后两者由原「首页」并入) | +> | 3 | 监控 | `/monitor` | 服务器 + 米家环境 | +> | 4 | 我 | `/me` | 用户信息 + 前端 OTA 升级 | +> +> 原「首页」Tab 已移除:其中的**报警**、**新闻** 两个模块并入宏观,作为其第 5、6 个子 Tab(`/macro/alerts`、`/macro/news`),新闻详情改为 `/macro/news/:id` 以保持宏观 Tab 高亮。 + +**不纳入 V0.2**:多用户共享清单、提醒推送、数据导出、打卡项分组、年视图热力图(见 §9 演进)。 + +### 1.2 架构总览 + +``` +┌──────────────────────── 前端 (React + antd-mobile, 打包进 Tauri / OTA) ────────────────────────┐ +│ App.jsx 路由 (默认 / → /checkin) │ +│ └ /checkin ──► CheckinPage ── SwipeTabs ──┬── HabitView (打卡) │ +│ ├── CalendarView (日历) │ +│ └── ListView (清单) │ +│ └ /macro/* ──► MacroPage ── 油价/经济数据/策略研究/经济文章/报警/新闻 │ +│ 组件: RingProgress(环形) / Celebrate(特效, 恒开) / BottomNav / TabIcon │ +│ 工具: src/pages/checkin/date.js (本地日期, 与后端同口径) apiClient: src/api/checkin.js │ +└──────────────────────────────────┬─────────────────────────────────────────────────────────────┘ + │ HTTPS + JWT (Authorization: Bearer) +┌──────────────────────────────────▼─────────────────────────────────────────────────────────────┐ +│ Express (server/index.js) │ +│ /api/checkin ─► routes/checkin.js ─┐ │ +│ /api/checklists─► routes/checklists.js ─┤─► middleware.authRequired ─► Prisma Client │ +│ └─► checkin/period.js (周期口径唯一来源) │ +└──────────────────────────────────┬─────────────────────────────────────────────────────────────┘ + ▼ + MariaDB: Habit / CheckIn / CheckList / CheckListItem / CheckListTemplate +``` + +### 1.3 分层与职责 + +| 层 | 位置 | 职责 | +| --- | --- | --- | +| 页面 | `src/pages/checkin/*.jsx` | 视图编排、本地状态、乐观更新与回滚 | +| 组件 | `src/components/RingProgress.jsx` | 纯展示:SVG 环形 + 完成特效(恒开,无开关) | +| 日期工具 | `src/pages/checkin/date.js` | 与后端同口径的本地日期运算(**改一处需同步另一处**) | +| API 封装 | `src/api/checkin.js` | 统一走 `api()`(自带 token / 错误解析) | +| 路由 | `server/routes/checkin.js`、`checklists.js` | 参数校验、鉴权、组装展示态 | +| 领域逻辑 | `server/checkin/period.js` | 周期窗口、日均期望、streak 判定 | +| 持久化 | Prisma Client(`generated/prisma`) | 表操作 | + +### 1.4 目录结构(本次新增/修改) + +``` +prisma/ + schema.prisma # +5 个 model,User 加反向关系 + migrations/20261022120000_add_checkin_checklist/ # 新建表 + 外键 +server/ + checkin/period.js # 周期计算(唯一口径) + routes/checkin.js # /api/checkin/* + routes/checklists.js # /api/checklists/* + index.js # 挂载两个路由 +src/ + api/checkin.js # checkinApi / checklistApi + api/ota.js # OTA 封装 + 判新模式(version/sequence) + utils/version.js # 版本号字段比较(纯函数, 可单测) + utils/format.js # 日期格式化 + components/RingProgress.jsx # 环形进度 + Celebrate 特效 + components/BottomNav.jsx # Tab: 打卡(默认)/宏观/监控/我 + components/TabIcon.jsx # 图标集(移除 home) + pages/checkin/CheckinPage.jsx # 容器:三视图切换 + pages/checkin/HabitView.jsx # 打卡视图 + pages/checkin/CalendarView.jsx # 日历视图 + pages/checkin/ListView.jsx # 清单视图 + pages/checkin/date.js # 前端日期工具 + pages/MacroPage.jsx # 宏观: 6 个子 Tab(含报警/新闻) + pages/macro/AlertsView.jsx # 报警(原首页) + pages/macro/NewsView.jsx # 新闻(原首页) + pages/MePage.jsx # 我: OTA 组内「开发版本」开关 + App.jsx # / → /checkin; /macro/*; 移除 /news/:id + styles/globals.css # 打卡相关样式段 +scripts/ci/check-version.mjs # CI: 校验分支名 ↔ package.json 版本 +docs/V0.2/ # 需求 / 设计 / 数据库设计 / 界面设计 +``` + +--- + +## 2. 领域模型与核心口径 + +### 2.1 实体 + +`Habit`(打卡项定义)· `CheckIn`(一次打卡)· `CheckList`(场景清单)· `CheckListItem`(检查项)· `CheckListTemplate`(模板)。字段与索引见《数据库设计文档》。 + +### 2.2 周期口径(唯一来源:`server/checkin/period.js`) + +| 概念 | 定义 | +| --- | --- | +| 本地日历日 | `Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai' })` → `"YYYY-MM-DD"` | +| 周起始 | **周一**(`startOfWeek` 把周日映射到第 7 天) | +| 周期窗口 | day = [d, d];week = [周一, 周日];month = [1 号, 月末];year = [1/1, 12/31] | +| **环形进度** | `percent = 周期内累计次数 / n`,上限 1 —— 回答「这周期做了多少」 | +| **日均期望次数** | day→n;week→`ceil(n/7)`;month→`ceil(n/当月天数)`;year→`ceil(n/365)` —— 回答「今天该做多少」 | +| 完成 | `done >= target`(超额允许,`over = done - target` 继续累加) | +| streak | 从当前周期向前回溯:达标 +1;**当前周期未结束且未达标 → 跳过而非中断**;已结束周期未达标 → 终止 | + +>两种口径并存是有意为之:环形看「周期整体」,日历日标记看「当天份额」。例如「每周 3 次」,周三打 1 次 → 环形 33.3%,当天已达标(1 ≥ ceil(3/7)=1)。 + +### 2.3 边界规则 + +| 场景 | 规则 | 编号 | +| --- | --- | --- | +| 提前打卡(未来日期) | 拒绝,返回 400「不能为未来日期打卡」 | F-CK-11 | +| 补打 | 仅限最近 **7 天**,记录 `source='backfill'` | F-CK-10 | +| 跨零点 | 以服务端 Asia/Shanghai 日历日为准,前端不自行判断 | E-02 | +| 周期中途新建 | 按完整 n 计算,不按剩余天数折算 | E-01 | +| 编辑 `target` | 立即生效,历史记录不改动 | F-CK-13 | +| 删除打卡项 | 默认归档(`archived=true`),`?hard=1` 物理删并清记录 | E-05 | +| 清单删除 | 物理删(一次性场景不留历史) | F-LS-10 | + +--- + +## 3. 前端设计 + +### 3.1 路由与容器 + +``` +底部 Tab (BottomNav) 打卡(默认) ─ 宏观 ─ 监控 ─ 我 + / → +/checkin → CheckinPage + ├─ NavBar「打卡」 + └─ SwipeTabs(左右滑动切换,与既有模块交互一致) + ├─ habit → HabitView (受 date 影响) + ├─ calendar → CalendarView (持有选中 date,可回写容器) + └─ list → ListView (独立) +/macro/* → MacroPage (油价 / 经济数据 / 策略研究 / 经济文章 / 报警 / 新闻) +``` + +`activeKeyFromPath()` 对未匹配路径兜底返回**第一个 Tab(`/checkin`)**,与默认路由保持一致。 + +`date` 状态提升到 `CheckinPage`:日历里点某天 → 切回打卡视图时即为该日,实现「日历与打卡联动」。 + +### 3.2 组件树与关键状态 + +``` +CheckinPage +├── useState: active('habit'|'calendar'|'list'), date('YYYY-MM-DD') +HabitView(date) +├── data: { today, date, items[], summary:{total,done} } +├── bursts: { habitId: n } // 特效触发计数(+1 即播放一次) +└── form / showForm // 新建/编辑弹窗 +CalendarView(date, onDateChange) +├── month, cal(月历), detail(当日), bursts +└── quickValue: -1|0|1|null // 昨天/今天/明天 分段控件 +ListView() +├── archived, lists, openId(展开的清单), newItem, templates, form/showForm +└── bursts +``` + +统一约定: +- 加载中 → `DotLoading`;错误 → `error-banner` + 重试;空 → antd-mobile `Empty` + 引导按钮(三态齐全,U-06)。 +- 页面重新可见时静默刷新(`useRefreshOnVisible`),与既有模块一致。 + +### 3.3 环形进度组件 `RingProgress` + +| 项 | 设计 | +| --- | --- | +| 实现 | 双 ``:灰底环 + `stroke-dasharray/dashoffset` 进度环,`rotate(-90)` 从 12 点顺时针 | +| 尺寸 | `size=58`,`stroke=6`,`r=26`,周长 `2πr≈163.4` | +| 动画 | `transition: stroke-dashoffset .45s cubic-bezier(.4,0,.2,1)`(400ms 级,U-01) | +| 中心内容 | 未达标 → `done / target` 数字;**达标 → 对勾 SVG**(`ring-check-in` 缩放入场);超额 → 右上角 `+n` 徽标 | +| 交互 | 整个环可点 = 打卡(`clickable`),同时卡片右侧保留显式「打卡」按钮,降低发现成本 | +| 无障碍/降级 | `disabled` 态半透明;**完成特效恒开**(V0.2 设计变更:取消页面内特效开关,无任何关闭入口) | + +**完成特效 `Celebrate`**(F-CK-07 / U-02):`fire` 计数器 +1 即触发一次 → 14 个粒子沿圆周角度扩散(`--a/--d` CSS 变量)+ 主色光环脉冲 0.7s,总时长 ≤900ms,`pointer-events:none` 不阻塞后续操作;同时 `navigator.vibrate(18)` 轻震动(U-03)。**特效永远播放**,不再读 `localStorage.iboard_celebrate`,也不再响应 `prefers-reduced-motion` 关闭(用户明确要求「特效永远打开」)。 + +### 3.4 交互清单 + +| 视图 | 主要交互 | +| --- | --- | +| 打卡 | 点环/点按钮打卡(乐观更新,失败回滚 + Toast)、撤销、编辑、归档、新建弹窗(名称/周期/目标次数/颜色/备注) | +| 日历 | 月份左右切换、回到今天、「昨天/今天/明天」分段切换、点日期选中、当日详情内打卡(未来日期只读显示「计划」)、本月完成率 | +| 清单 | 进行中/已归档切换、展开/收起、逐条勾选、添加检查项、全选/全不选、复制、存为模板、归档/恢复、删除、从模板新建 | + +排序:打卡项列表**未完成在前**(F-CK-16);清单按 `updatedAt` 倒序。 + +### 3.5 视觉规范 + +| 类别 | 取值 | +| --- | --- | +| 主色 | `#1677ff`;成功 `#00b578`;警告/部分 `#ffc53d`;超额 `#a855f7`;危险 `#ff3141` | +| 背景 / 卡片 | `#f5f5f5` / `#ffffff`,卡片 `radius 10px` + `0 1px 2px rgba(0,0,0,.04)` | +| 文字 | 主 `#333`、次 `#666`、弱 `#999` | +| 日历日状态点 | full 绿 / partial 黄 / over 紫 / empty 无 | +| 打卡项预设色 | `#1677ff #00b578 #ff8f1f #ff3141 #a855f7 #14b8a6 #f43f9e` | + +完整高保真稿见 `界面设计.html`。 + +--- + +## 4. 后端设计 + +### 4.1 通用约定 + +- 两个路由文件首行 `router.use(authRequired)`,**全部接口需登录**;用户维度一律取 `req.user.sub`,不信任 body 中的 `userId`。 +- 响应统一 `{ item }` / `{ items }` / `{ ok }`;错误统一 `{ error: '中文提示' }` + 语义化状态码。 +- 展示态(percent / completed / streak / dayDone …)由服务端计算后下发,前端不做二次口径推导,避免两端不一致。 + +### 4.2 接口清单 + +#### 打卡 `/api/checkin` + +| 方法 | 路径 | 说明 | 关键响应字段 | +| --- | --- | --- | --- | +| GET | `/habits?date=` | 指定日待办 + 周期进度 | `items[]`(含 `done/target/percent/completed/over/streak/expectedPerDay/periodStart,End`)、`summary` | +| POST | `/habits` | 新建打卡项 | `item` | +| PUT | `/habits/:id` | 编辑(名称/周期/目标/颜色/备注/归档/排序) | `item` | +| DELETE | `/habits/:id?hard=1` | 默认归档;`hard=1` 物理删 | `{ ok, removed }` | +| POST | `/habits/:id/check` | 打一次卡(`{ date?, note? }`) | `item`(最新展示态) | +| DELETE | `/habits/:id/check?date=` | 撤销本周期最近一次 | `item` | +| GET | `/day?date=` | 某日每个打卡项明细 | `items[]`(含 `canCheck`)、`isToday`、`isFuture` | +| GET | `/calendar?month=YYYY-MM` | 月历 + 完成率 | `days[]`(`status/expected/actual/isToday/isFuture/habits[]`)、`rate`、`habits[]` | + +#### 清单 `/api/checklists` + +| 方法 | 路径 | 说明 | +| --- | --- | --- | +| GET | `/?archived=0|1` | 清单列表(带 `done/total/percent/completed`) | +| POST | `/` | 新建(`{ title, scene, note?, items?, templateId? }`) | +| GET | `/:id` | 单条详情 | +| PUT | `/:id` | 改名称/场景/备注/归档 | +| DELETE | `/:id` | 物理删除(连带检查项) | +| POST | `/:id/duplicate` | 复制为新清单(勾选状态重置) | +| POST | `/:id/items` | 新增检查项 | +| PUT | `/items/:id` | 改标题/备注/勾选(`done` 变更时写 `doneAt`) | +| DELETE | `/items/:id` | 删除检查项 | +| POST | `/:id/check-all` | 一键全选/全不选(`{ done }`) | +| GET | `/templates` | 模板列表(含系统预置,首次访问幂等写入) | +| POST | `/templates` | 另存为模板 | +| DELETE | `/templates/:id` | 删除**自建**模板(预置模板拒绝) | + +### 4.3 关键流程:打卡 + +``` +用户点环 + │ + ├─ 前端: 本地 +1,重算 percent/completed/over(乐观更新) + │ + └─ POST /api/checkin/habits/:id/check + ├─ 校验 habit 归属 → 404 + ├─ date > today → 400 不能为未来日期打卡 + ├─ date < today-7 → 400 最多只能补打最近 7 天 + ├─ seq = max(seq)+1 → insert + │ └─ P2002 → 409 该次打卡已记录(幂等) + └─ 返回最新 item + │ + ├─ 成功: 用服务端 item 覆盖本地;若 completed → fire 特效 + └─ 失败: setData(before) 回滚 + Toast +``` + +### 4.4 关键流程:月历聚合 + +1. 取全部 Habit(含归档)→ 过滤 `archived=false` 得 `active`。 +2. 一次查询当月 `[01, 月末]` 全部 CheckIn,按 `habitId → date → count` 分组。 +3. 逐日遍历:仅统计 `Habit.createdAt <= 该日` 的项;累加 `expected += expectedPerDay(...)`、`actual += min(count, expected)`;任一 `count > expected` 记 `over`。 +4. `status`:全完成且无超额 → `full`;全完成且有超额 → `over`;有完成未全完成 → `partial`;否则 `empty`。 +5. 完成率 `rate = Σactual / Σexpected`(**只统计非未来日期**)。 + +复杂度:`O(天数 × 打卡项数)`,30×20=600 次内存运算,无 N+1 查询。 + +### 4.5 校验与错误码 + +| 状态码 | 场景 | +| --- | --- | +| 400 | 名称为空/超长、周期非法、target < 1、未来日期打卡、超出补打窗口、本周期无记录可撤销、模板缺条目 | +| 404 | 资源不存在或不属于当前用户 | +| 409 | 并发重复打卡(P2002) | +| 401 | 未登录 / token 失效(`authRequired`) | + +所有写操作均用 `prisma.xxx.findFirst({ where: { id, userId } })` 或 `where: { id, list: { userId } }` 做归属校验,杜绝越权改他人数据。 + +--- + +## 5. 非功能设计 + +| 维度 | 设计 | +| --- | --- | +| 首屏 | 三视图懒切换,只有激活视图发请求;单视图首屏 ≤ 1 次列表请求(N-01) | +| 弱网/失败 | 乐观更新 + 失败回滚 + Toast;错误横幅带重试;`useRefreshOnVisible` 静默刷新 | +| 幂等 | 唯一约束 `(habitId, date, seq)` + 前端按钮 loading,防连点重复计次 | +| 时区 | 全链路 Asia/Shanghai;库内 `DATETIME` 存 UTC,业务日期用 `VARCHAR(10)` 本地日历日 | +| 兼容 | 375~430px 自适应;Tauri Android WebView 支持 `navigator.vibrate`,不支持时静默跳过 | +| 性能 | 月历/周期统计均为「一次范围查询 + 内存聚合」,避免逐项查询 | +| 安全 | 全接口 `authRequired`;输入长度截断(名称 20、检查项 40、备注 200);无 SQL 拼接(Prisma 参数化) | +| 可维护 | 周期口径单点(`server/checkin/period.js`),前端 `date.js` 与之对齐,改动需同步两处(已在文档与注释中标注) | + +--- + +## 6. 测试与验证 + +| 层级 | 内容 | 结果 | +| --- | --- | --- | +| 单元 | 周期工具 23 条断言(窗口边界、周一起始、月末、闰年、日均期望、streak 跳过未结束周期) | 全通过 | +| 单元 | 版本比较 10 条断言(`-ota` 后缀、`v` 前缀、缺位补 0、`0.10 > 0.9`、`beta.3 < beta.10`、空串) | 全通过 | +| 接口冒烟 | 自签 JWT 端到端:每天 1 次闭合、每周 3 次 33.3%、超额 +1、撤销归零、未来日期 400、月历 31 天、模板生成 11 项、全选完成、复制重置 | 全通过,测试数据已清理 | +| 构建 | `npm run build` | 通过 | +| 迁移 | `prisma migrate deploy` 到目标库 | 成功 | + +--- + +## 7. 里程碑(对应需求文档 M1~M5) + +| 里程碑 | 内容 | 状态 | +| --- | --- | --- | +| M1 | 数据模型 + 迁移 + 周期模块 + 全部后端接口 | 完成 | +| M2 | 打卡视图:环形进度、特效、撤销/补打、新建编辑 | 完成 | +| M3 | 日历视图:月历 + 昨天/今天/明天 + 完成率 | 完成 | +| M4 | 清单视图:CRUD、勾选、进度、模板复用 | 完成 | +| M5 | 文档(需求/设计/数据库/界面)+ 构建校验 + 提交 | 完成 | + +--- + +## 8. 风险与权衡 + +| 项 | 权衡 | 缓解 | +| --- | --- | --- | +| 周期口径前后端各实现一份 | 前端需离线即时反馈,不能等接口 | 两端算法同源同注释;口径变更必须同步 `period.js` 与 `date.js`(已写入项目记忆) | +| 进度实时聚合而非冗余计数 | 换取一致性与配置即时生效,牺牲少量 CPU | 单用户数据量极小(月历 30 天 × 20 项),实测无感;V0.3 可加汇总表 | +| 未来日期禁止打卡 | 防止「提前打完一周」刷进度,符合「日历看明天=计划」语义 | 明天视图只读展示「计划」,明确文案 | +| 特效恒开可能打扰 | 用户明确要求「特效永远打开」,不再提供开关 | 单次特效 ≤900ms 且 `pointer-events:none`,不影响连续打卡;若后续需要再评估恢复开关 | +| Tab 调整为 4 个(打卡置左并默认) | 首页被取消,报警/新闻并入宏观后宏观有 6 个子 Tab | 子 Tab 可横向滚动;报警角标保留在子 Tab 上;底部 4 项在 375px 下热区充足(≥44px) | + +--- + +## 9. 后续演进(V0.3+) + +1. **提醒与通知**:`Habit.remindAt` + Tauri 本地通知,解决「忘打卡」。 +2. **补打放宽 / 历史编辑**:当前 7 天限制可配置化。 +3. **统计视图**:连续天数榜单、月/年热力图(届时引入 `HabitDailyStat` 汇总表)。 +4. **打卡项分组与排序拖拽**:现有 `sortOrder` 已预留,仅需交互。 +5. **清单协作/分享**:引入 `CheckListShare`。 +6. **数据导出**:清单完成后导出为图片或文本(便于发群里确认)。 + +--- + +## 10. 设计变更记录(2026-10-22) + +### 10.1 变更清单 + +| # | 类型 | 变更 | 影响文件 | +| --- | --- | --- | --- | +| 1 | Bug | OTA 包版本与分支不对应:`dev-0.2` 上仍是 `0.1.0` → OTA 报成 `0.1.0-ota`。统一升到 `0.2.0`,OTA 包变为 **`0.2.0-ota`**,并加 CI 强校验 | `package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml`、`Cargo.lock`、`scripts/ci/check-version.mjs`、`docker-build.yml` | +| 2 | 功能 | 我的页 OTA 组新增**「开发版本」Switch**(默认关),切换判新口径:开=`build sequence`,关=版本号字段比较;开关值持久化在设备端 | `src/api/ota.js`、`src/utils/version.js`、`src/pages/MePage.jsx` | +| 3 | 设计 | 打卡 Tab 移到**最左边并默认选中**;打卡页移除**特效开关**,特效永远打开 | `BottomNav.jsx`、`App.jsx`、`TabIcon.jsx`、`HabitView.jsx`、`RingProgress.jsx`、`globals.css` | +| 4 | 设计 | 取消「首页」Tab,其**报警 / 新闻**并入宏观,成为宏观的第 5、6 个子 Tab | `MacroPage.jsx`、新增 `macro/AlertsView.jsx`、`macro/NewsView.jsx`、删除 `HomePage.jsx` | + +### 10.2 版本号为什么必须跟分支走 + +`scripts/build-ota.mjs` 里 `manifest.version = package.json.version + '-ota'`,也就是**版本号的唯一来源仍是 `package.json`**。 +分支按版本命名(`dev-0.2`),但版本号是手工改的,一旦忘改就会出现「分支 0.2、包版本 0.1」的错位,客户端判新逻辑全部失准。 + +**同一版本号的两种产物写法**(2026-10-22 补充确认): + +``` +分支 dev-0.2 → 0.2.0 + ├─ APK / 桌面安装包 0.2.0 裸版本号,无后缀 + ├─ 前端运行时 0.2.0 裸版本号,无后缀 + └─ OTA 热更包 0.2.0-ota 多一个 -ota 标记 +``` + +即 APK 与 OTA 的版本主体完全一致,唯一差别是 OTA 多 `-ota` 后缀;`package.json` 只写裸版本号,后缀由打包脚本追加。 + +| 环节 | 取值 | 现状 | +| --- | --- | --- | +| `package.json` `version` | `0.2.0` | 唯一手动管理点,**不得带 `-ota`** | +| `src-tauri/tauri.conf.json` `version` | `0.2.0` | 决定 **APK `versionName`**(经 `tauri android init` 写入 `tauri.properties`)与桌面包版本 | +| `src-tauri/Cargo.toml` / `Cargo.lock` | `0.2.0` | Rust 包版本,与上两者保持一致 | +| OTA `latest.json.version` | `0.2.0-ota` | 打包脚本自动拼 `-ota` 后缀 | +| `src/version.js` `APP_VERSION` | `0.2.0` | 前端展示与版本比较的基准 | +| APK `versionCode` | `2000` | `major*1000000 + minor*1000 + patch`,必须单调递增才能覆盖安装(`0.1.0` = 1000) | +| OTA `latest.json.min_binary_version` | `0.1.0` | **与前端版本解耦**:前端 bundle 要求的最低原生二进制版本。现有 APK 原生层仍是 0.1.0,跟着抬会导致旧 APK 收不到更新,CI 固定传 `--min-binary 0.1.0` | +| CI 守卫 | `scripts/ci/check-version.mjs` | ① 四处版本必须完全相等且不含 `-ota`(`main`/`master` 也查);② 从 `GITHUB_REF_NAME` 解析 `dev-.`,要求 `version` 以该前缀开头;任一不满足**构建直接失败**。`docker-build.yml` 与 `android-build.yml` 都在构建前执行 | + +### 10.3 判新口径:版本号 vs build sequence + +「我的 → 前端升级 (OTA)」组内新增 `开发版本` 开关(antd-mobile `Switch`),默认**关闭**: + +| 开关 | 判新依据 | 实现路径 | 适用场景 | +| --- | --- | --- | --- | +| **关(默认)** | **版本号字段大小** | `GET /api/ota/check/0` 取 `manifest.version` → `compareVersion(manifest.version, 当前运行版本) > 0` 才算新 | 生产口径。旧分支被重新构建(sequence 变大但版本更小)时不会把设备拉回旧版 | +| **开** | **build sequence** | `tauri-plugin-hotswap` 的 `checkUpdate()`(内部拿本地 sequence 请求 `/api/ota/check/:seq`,服务端只回比它大的) | 开发联调。每次构建 sequence 都变,保证任何一次提交都能推到设备上 | + +补充约定: + +- **持久化**:`localStorage['iboard_ota_dev_mode'] = '1' | '0'`(默认 `'0'`)。值只存在设备端 WebView 存储里,不进数据库、不上报,换设备/清数据即回默认。 +- **当前运行版本**:已应用过 OTA 覆盖包时取覆盖包 `version`(插件 `getVersionInfo()`),否则取内置包 `APP_VERSION`;避免「内置包 0.1.0 + 覆盖包 0.2.0」时永远判定有新版本。 +- **切换开关会重置检查结果**(`otaState='idle'`、清空 `otaInfo`),避免上一种口径的结论误导用户。 +- **Web 端**:浏览器里拿不到本地 sequence。默认口径按版本号比较;开发口径退化为「服务端版本 ≠ 本地版本即算新」(近似 sequence 语义)。Web 端只提示刷新,不下载不替换,与原设计一致。 + +**版本比较算法**(`src/utils/version.js`,纯函数可单测): + +```js +parseVersionFields('0.2.0-ota') // → [0, 2, 0] +compareVersion('0.2.0-ota','0.1.0') // → 1 (0.2.0 更新) +compareVersion('0.2','0.2.0') // → 0 (缺位补 0) +``` + +规则:按非数字字符切段 → 过滤空段 → 转数字 → 逐段比较,位数不足补 0。单测 10 条覆盖 `-ota` 后缀、`v` 前缀、多位段(`0.10 > 0.9`)、空串、`beta.3 < beta.10`,全部通过。 + +### 10.4 交互与信息架构变更 + +| 项 | 变更前 | 变更后 | +| --- | --- | --- | +| 底部 Tab | 首页 / 宏观 / 监控 / 打卡 / 我(5 个) | **打卡 / 宏观 / 监控 / 我(4 个)**,打卡在最左 | +| 默认落地 | `/` → 首页(默认选中新闻) | `/` → **`/checkin`**;`RedirectIfAuthed` 与 404 兜底同样指向 `/checkin` | +| 报警 / 新闻 | 首页内的 2 个 Tab | 宏观的第 5、6 个子 Tab(`/macro/alerts`、`/macro/news`),报警角标保留 | +| 新闻详情 | `/news/:id`(底部无 Tab 高亮) | **`/macro/news/:id`**(宏观 Tab 保持高亮) | +| 打卡页头部 | `[特效开/关]` + `[+ 新建]` | 只保留 `[+ 新建]`;特效恒开 | +| 特效实现 | `effectsEnabled()` 读 `localStorage.iboard_celebrate` + `prefers-reduced-motion` 降级 | 删除开关与降级判断,`Celebrate` 只要 `fire` 变化就播放 | + +### 10.5 界面设计同步 + +`界面设计.html` 已同步:底部 Tab 改为 4 项且「打卡」高亮在首位、打卡页去掉特效开关按钮、新增「我的 · OTA 开发版本开关」屏、宏观新增「报警 / 新闻」子 Tab 与合并后的 6 Tab 导航。 + +--- + +## 11. 界面调整(2026-10-23) + +### 11.1 变更清单 + +| # | 范围 | 变更 | 状态 | +| --- | --- | --- | --- | +| 1 | 页面头部 | 打卡 NavBar 高度减半(45 → 23px)、标题改主色 12px;宏观 / 监控补标题;「我」页整条去掉 | **已实现** | +| 2 | 打卡页 | 日历与打卡合并为一个视图(上日历、下打卡,按天/周/月分组、去掉年分组、卡片紧凑化) | **待确认**(设计稿已出,见《界面设计.html》2.12 ②节) | +| 3 | 清单页 | 新增「模板」分类(与进行中 / 已归档并列),独立 CRUD + 字母序;新建清单去掉场景、模板改右侧滑出选择 | **已实现** | +| 4 | 宏观页 | 报警 / 新闻移到最左并默认选中新闻;tab 条增加右侧溢出提示 | **已实现** | + +### 11.2 页面头部统一 + +新增组件 `src/components/PageHeader.jsx`(高度 23px、标题 12px/600 主色、字距 1px、背景取页面底色 `#f5f5f5`),只被 **打卡 / 宏观 / 监控** 三页引用: + +| 页 | 变更前 | 变更后 | +| --- | --- | --- | +| 打卡 | `NavBar` 45px,标题 17px 深色 | `PageHeader` 23px,标题主色 | +| 宏观 | `NavBar` 45px,空白 | `PageHeader` + 标题「宏观」 | +| 监控 | `NavBar` 45px,空白 | `PageHeader` + 标题「监控」 | +| 我 | `NavBar` 45px 空白条 | **移除**,`me-profile` 直接贴顶 | + +> 「我」页不引用该组件,因此调整打卡/宏观/监控的头部不会波及「我」;反之亦然。三个页面之间也各自持有独立实例,互不影响。 + +### 11.3 清单模板分类 + +**接口**(`server/routes/checklists.js`): + +| 方法 | 路径 | 说明 | +| --- | --- | --- | +| GET | `/api/checklists/templates` | 列表,**服务端按名称字母顺序排序**(原为 `builtin desc, id asc`) | +| GET | `/api/checklists/templates/:id` | 详情(新增) | +| POST | `/api/checklists/templates` | 新建(名称 ≤20 字,≥1 条检查项) | +| PUT | `/api/checklists/templates/:id` | 改名称 / 检查项(新增;`builtin=true` 拒绝,404) | +| DELETE | `/api/checklists/templates/:id` | 删自建模板(`builtin=true` 拒绝) | + +排序口径:`Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' })` —— 中文按拼音、数字按数值。前端 `ListView` 拿到列表后再排一次,保证与后端一致(网络返回顺序不可控时也不乱)。 + +**前端**(`src/pages/checkin/ListView.jsx`): + +- 分段由 2 项扩为 3 项:`active`(进行中)/ `archived`(已归档)/ `template`(模板);右上角按钮随分段切换为「+ 新建」/「+ 新建模板」。 +- 模板卡片:名称 + 「预置」标记 + 项数;最多展示 4 条检查项摘要,超出折叠为 `+n`;操作「由此新建 / 编辑 / 删除」,预置模板只保留「由此新建」。 +- 新建 / 编辑模板共用底部弹层:名称 + 检查项逐条编辑(≤40 字,可删可加)。 +- **新建清单表单**:仅「名称」+「模板」两项 —— **场景选择器已移除**(`scene` 改为沿用所选模板自带的场景,无模板则为空;数据库字段保留,历史数据照常展示)。 +- **模板选择**:点击模板行从**右侧滑出** `78vw` 面板(antd-mobile `Popup position="right"`),首项固定「不使用模板」,其后为字母序模板列表(预置带标记 + 项数),选中项主色高亮。 + +### 11.4 宏观 tab 顺序与溢出提示 + +- Tab 顺序改为 **报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章**,默认选中 **新闻**;`/macro` 由路由直接 `Navigate` 到 `/macro/news`(`App.jsx`),`activeFromPath` 兜底同样返回 `news`。 +- `SwipeTabs` 增加溢出提示层:测量 `.adm-tabs-tab-list` 的 `scrollLeft + clientWidth < scrollWidth`,为真时在顶部右侧渲染 `.tab-more-hint`(白色渐变 + 主色圆底箭头「›」,1.5s 轻微摆动动画,点击向右翻页 60% 视宽),滚到末尾自动消失。 +- 切换 tab 时把激活项平滑滚到可视区中央,避免选中项藏在屏幕外。 +- 为什么不用框架自带的:antd-mobile Tabs 自带 `.adm-tabs-header-mask-right` 只有 30px 纯渐变、无箭头、无交互,用户难以判断"右侧还有内容",故叠加此提示层。 + +### 11.5 待确认项:日历与打卡合并 + +方案已写入《界面设计.html》2.12 ②节(含主视图、未来日期计划态、卡片「⋯」操作面板三屏)。要点: + +| 项 | 方案 | +| --- | --- | +| 结构 | SwipeTabs 由 3 个减为 2 个:`日历(含打卡)` / `清单` | +| 日历 | 月历 + 月份切换 + 回到今天;**去掉「昨天/今天/明天」分段**;日期格 38 → 31px | +| 打卡 | 沿用现有卡片样式,紧凑化:内边距 12→8px、环形 58→42px(环宽 6→5)、间距 10→7px | +| 操作 | 撤销 / 编辑 / 归档收进「⋯」底部面板(原为卡片底部三个文字链接) | +| 分组 | **每天 → 每周 → 每月**,组标题左侧主色竖条;**去掉「每年」分组** | +| 联动 | 点日期即切换当日数据;未来日期只读,按钮灰显「计划」 | + +确认后再落地:`CheckinPage` 收敛为 2 个 tab、`CalendarView` 与 `HabitView` 合成新视图组件、`HabitView` 的循环/编辑逻辑抽出复用、样式新增 `.mv-*` 段。 + + diff --git a/docs/V0.2/需求文档.md b/docs/V0.2/需求文档.md new file mode 100644 index 0000000..b8585a9 --- /dev/null +++ b/docs/V0.2/需求文档.md @@ -0,0 +1,348 @@ +# iBoard V0.2 需求文档 — 打卡 / 日历 / 清单 + +> 版本:V0.2 · 分支:`dev-0.2` · 日期:2026-10-22 +> 目标项目:iboard(Tauri 2 + React 18 + antd-mobile + Express + Prisma/MariaDB,移动端优先) +> 基线版本:V0.1(`dev-0.1`) + +--- + +## 1. 总体描述 + +在 iBoard 移动端新增一个「打卡」模块一个「清单」模块,满足两类日常需求: +### 打卡 +1. **周期性打卡**(习惯养成):用户定义"每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次"的目标, + 每次打卡后卡片上的**环形进度**按 `已完成次数 / 目标次数` 增长,达标时环形闭合并播放完成特效; +2. **日历回看**:以日历为载体,快速查看**今天**(要做什么)、**昨天**(做得怎样)、 + **明天**(计划做什么),日期上以圆点/实心标记完成情况; + +### 清单 +1. **清单**(场景化一次性事项):面向**出差、回老家**等临时场景,逐条勾选确认, + 避免遗漏;清单可复用为模板,下次一键生成。 + +### 1.1 本期范围 + +- 单用户使用,数据按登录用户隔离(复用现有 JWT 鉴权)。 +- 移动端优先(`MobileShell` + 底部 TabBar 体系),桌面浏览器兼容可用即可。 +- 数据落库 MariaDB,通过现有 Express `/api/*` 提供接口。 + +### 1.2 不在本期范围(明确排除) + +- 多人协作 / 共享打卡 / 打卡排行榜。 +- 系统通知、推送提醒、定时提醒(V0.2 不引入通知能力)。 +- 复杂统计报表(周报/月报/趋势图),本期只提供完成率概览。 +- 桌面端专用布局、横屏适配。 +- 清单的周期性重复(如"每次出差都生成"),本期只做**临时清单 + 模板手动复用**。 + +--- + +## 2. 术语与概念定义 + +| 术语 | 定义 | +| --- | --- | +| **打卡项(Habit)** | 一个被跟踪的周期性目标,如"喝水 每天 8 次""健身 每周 3 次" | +| **周期类型** | `day` / `week` / `month` / `year`,决定进度统计窗口 | +| **目标次数 n** | 一个周期内需要完成的打卡次数,n ≥ 1 | +| **周期窗口** | 日=自然日;周=自然周(周一 00:00 至周日 23:59);月=自然月;年=自然年。时区统一 Asia/Shanghai | +| **打卡记录(CheckIn)** | 一次打卡动作,归属到某个**本地日历日**(可补打到过去日期) | +| **环形进度** | 卡片上的圆环组件,`进度 = min(已完成次数 / n, 1)`;达标=闭合 | +| **清单(CheckList)** | 一次性检查清单,如"出差 · 10月杭州",含若干检查项 | +| **检查项(CheckItem)** | 清单中的单条待检查事项,勾选即完成 | +| **模板(Template)** | 可被复用的清单结构(一组检查项标题),用于一键生成新清单 | +| **日期状态** | 某日的打卡完成情况:全部完成 / 部分完成 / 未开始 / 超额完成 | + +--- + +## 3. 需求点 + +**编号规则**:`类型-模块-序号`。类型:F=功能需求、U=交互与体验、D=数据、N=非功能、E=异常与边界。 +模块:CK=打卡、CL=日历、LS=清单、SY=通用。 + +**优先级**:P0 = 本期必须有;P1 = 本期应该有;P2 = 有则更好(可延后)。 + +### 3.1 功能需求 — 打卡(F-CK) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| F-CK-01 | 支持新建打卡项:名称、周期类型、目标次数 n、颜色、图标、备注 | 表单校验:名称必填且 ≤ 20 字;n 为 ≥1 的整数 | P0 | +| F-CK-02 | 周期类型支持**每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次**四种 | 四种类型均可创建并正确统计 | P0 | +| F-CK-03 | 卡片以**环形进度**展示当前周期完成情况,环中心显示 `已完成/n` | 进度 = 已完成次数 / n,上限 100% | P0 | +| F-CK-04 | **每天 1 次**的打卡项:打卡后环形**直接闭合**(100%),卡片进入"已完成"态 | 一次点击即闭合,不出现 50% 之类的中间态 | P0 | +| F-CK-05 | **每周 n 次**(及月/年):每打卡一次环形闭合对应百分比(如 n=3,打 1 次=33.3%、2 次=66.7%、3 次=100% 闭合) | 进度随每次打卡实时刷新,带过渡动画 | P0 | +| F-CK-06 | 打卡主操作:点击卡片上的打卡按钮即完成一次打卡 | 单击即生效,响应 < 300ms(乐观更新) | P0 | +| F-CK-07 | 打卡**完成特效**:达标(进度达 100%)时播放特效——环形闭合动画 + 光晕/粒子扩散 + 轻量触感反馈 | 特效时长 ≤ 800ms,不阻塞后续操作 | P0 | +| F-CK-08 | 打卡记录需记录**发生日期**(本地日历日)与时间戳 | 支持按日回看,跨零点归属正确 | P0 | +| F-CK-09 | 支持**撤销**:可撤销本周期内最近一次打卡(主要是误触场景) | 撤销后进度回退,记录物理删除或标记撤销 | P1 | +| F-CK-10 | 支持**补打卡**:对过去日期补记一次打卡,默认允许回溯 7 天 | 补打后对应日期的状态与进度同步更新 | P1 | +| F-CK-11 | **不允许提前打卡**(未来日期不可打卡) | 明天的项只展示为"计划",按钮置灰 | P0 | +| F-CK-12 | 展示**连续完成天数/周期数(streak)** | 如"已连续 5 天",断签归零 | P1 | +| F-CK-13 | 打卡项可**编辑**(改名/改 n/改颜色/改周期)与**停用(归档)** | 归档后不出现在今日待办,历史保留 | P1 | +| F-CK-14 | 修改目标次数 n 后,**当前周期**进度按新 n 重算展示,不改写历史记录 | 例如 n 由 3 改 5,已完成 3 次 → 显示 3/5(60%) | P1 | +| F-CK-15 | **超额打卡**:周期内超过 n 仍可继续打卡,环形保持闭合,中心显示 `n+k`(如 3+1) | 不清零、不报错 | P2 | +| F-CK-16 | 打卡项支持**排序**(拖动或置顶)与分组展示 | 今日列表按"未完成在前、已完成在后"默认排序 | P2 | +| F-CK-17 | 打卡时可附带**备注**(可选,纯文本) | 列表详情中可查看 | P2 | + +### 3.2 功能需求 — 日历(F-CL) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| F-CL-01 | 提供**月历视图**:当月日历网格,可切换上/下月,点击日期查看当日详情 | 周一起始,周一~周日七列 | P0 | +| F-CL-02 | **今天**视图(默认):列出今天应打卡的项及完成状态,可直接打卡 | 打开页面默认定位今天 | P0 | +| F-CL-03 | **昨天**视图:展示昨天各打卡项的**已完成结果**(完成/未完成/超额),未完成项可进行补打 | 以回看为主,标记为"已完成的历史" | P0 | +| F-CL-04 | **明天**视图:展示**明天计划要打卡的项**(按周期规则推算),只读,不可打卡 | 明确标注"计划",打卡按钮不可用 | P0 | +| F-CL-05 | 今天/昨天/明天**快捷切换**(顶部 Segmented 或左右滑动) | 一键切换,切换有过渡动画 | P0 | +| F-CL-06 | **日期标记**:月历中每个日期按完成情况标记——全部完成(实心)、部分完成(半填充)、未开始(空心/无标记)、超额完成(特殊标记) | 一眼能看出当月坚持情况 | P0 | +| F-CL-07 | **日期详情**:点击某日期,展示当天每个打卡项的"目标次数 / 已完成次数 / 状态" | 过去日期可补打,未来日期只读 | P0 | +| F-CL-08 | 日历与打卡**数据同源**:打卡操作在日历视图与打卡列表视图中即时一致 | 任一处打卡,另一处刷新 | P0 | +| F-CL-09 | 月历顶部展示**当月完成率**(完成次数 / 应完成次数) | 百分比展示 | P1 | +| F-CL-10 | 支持左右**滑动手势**切换月份 | 与按钮切换等价 | P2 | +| F-CL-11 | 月视图支持简单的**完成热力**(按完成率深浅着色) | 可选展示,默认关闭 | P2 | + +### 3.3 功能需求 — 清单(F-LS) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| F-LS-01 | 支持新建**检查清单**:名称、场景标签(出差 / 回老家 / 自定义)、备注 | 名称必填,场景标签可空 | P0 | +| F-LS-02 | 清单内可**增删改检查项**,支持排序 | 单条 ≤ 40 字 | P0 | +| F-LS-03 | **勾选完成**:点击检查项前的复选框即打勾,已完成项置灰 + 删除线;可取消勾选 | 单击切换,状态即时保存 | P0 | +| F-LS-04 | 清单头部显示**进度**:`已完成 / 总数` + 进度条;全部完成时显示完成态 | 全部勾完触发完成提示 | P0 | +| F-LS-05 | 清单**全部完成**时播放完成特效(与 F-CK-07 同一套特效组件) | 复用,风格统一 | P1 | +| F-LS-06 | **模板**:系统预置常用模板(出差、回老家),一键基于模板生成新清单 | 至少 2 个预置模板 | P1 | +| F-LS-07 | **另存为模板**:用户可将自己的清单保存为自定义模板,后续复用 | 自定义模板可删除 | P1 | +| F-LS-08 | **复用历史清单**:从历史清单复制生成一份新清单(勾选状态重置) | 原清单保持不变 | P1 | +| F-LS-09 | 清单**归档/删除**:完成的清单可归档(不出现在进行中列表),可查看历史 | 删除需二次确认 | P1 | +| F-LS-10 | 清单项支持**备注/数量**说明(如"充电器 ×1") | 展示在标题下方,弱色小字 | P2 | +| F-LS-11 | 清单支持**一键全部勾选 / 全部取消** | 批量操作入口 | P2 | + +### 3.4 功能需求 — 通用(F-SY) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| F-SY-01 | 底部 TabBar 新增**「打卡」页**,并作为**默认首页放在最左**;「清单」不单独占 Tab,作为打卡页内的第三个子视图 | 图标风格与现有 TabIcon 一致;启动即进入 `/checkin` | P0 | +| F-SY-02 | 「打卡」页内集成 打卡/日历 两个功能,日历放在上半截,默认显示一行为周视图,下拉显示为月视图。 | 日历中选择时间与下部打卡任务联动 | P0 | +| F-SY-03 | 所有接口复用现有鉴权(`authRequired`,JWT),数据按 `userId` 隔离 | 未登录跳转 `/login` | P0 | +| F-SY-04 | 后端新增 REST 路由(建议 `/api/checkin/*`、`/api/checklist/*`),挂载到 `server/index.js` | 与现有路由风格一致 | P0 | +| F-SY-05 | 时间处理统一 **Asia/Shanghai**,前端与后端对"日历日"的判定一致 | 跨零点打卡归属当天 | P0 | +| F-SY-06 | 打卡/勾选操作采用**乐观更新**,接口失败时回滚并 Toast 提示 | 失败可见、状态不失真 | P1 | +| F-SY-07 | 写接口**幂等/防抖**:同一打卡项 1 秒内重复点击只记一次 | 前端防抖 + 后端唯一约束双保险 | P1 | +| F-SY-08 | 页面可见性变化时刷新数据(复用 `useRefreshOnVisible`) | 从后台切回自动同步 | P1 | + +### 3.5 交互与体验需求(U) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| U-01 | **环形进度组件**:SVG 圆环,背景环 + 进度环,进度变化带 400ms ease-out 动画 | 组件可复用(打卡卡片、清单进度共用) | P0 | +| U-02 | **完成特效**:达标瞬间环形闭合 + 光晕/粒子扩散,约 600ms;可重复观看不烦扰 | 不阻塞操作,动画结束自动清理 | P0 | +| U-03 | **触感反馈**:完成打卡时轻量震动(Web Vibration API / Tauri) | 固定开启,与特效一同触发 | P1 | +| U-04 | 移动端适配:375~430px 视口,复用 `globals.css` 的 `.card` 体系,安全区与底部 TabBar 不遮挡内容 | 真机无横向滚动、无遮挡 | P0 | +| U-05 | 空态 / 加载态 / 错误态三态齐全(首次使用引导"创建第一个打卡项") | 空态有明确 CTA | P0 | +| U-06 | 打卡按钮足够大(点击热区 ≥ 44×44),符合移动端触控规范 | 误触率低 | P1 | +| U-07 | 动效**性能降级**:特效保持轻量(单次 ≤900ms、`pointer-events:none`),不给低端机造成卡顿 | 不卡顿、不阻塞操作 | P1 | +| U-08 | 深浅色:跟随现有浅色主题,卡片/分割线风格与首页、监控页一致 | 视觉统一 | P1 | +| U-09 | 清单勾选有明确的勾选动画(勾形描边动画) | 反馈即时 | P2 | + +### 3.6 数据需求(D) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| D-01 | 新增 Prisma 模型:`Habit`(打卡项)、`CheckIn`(打卡记录) | 字段见附录 A | P0 | +| D-02 | 新增 Prisma 模型:`CheckList`(清单)、`CheckListItem`(检查项)、`CheckListTemplate`(模板,P1) | 字段见附录 A | P0 | +| D-03 | `Habit` / `CheckList` 均带 `userId` 外键,删除用户级联删除 | 数据隔离 | P0 | +| D-04 | `CheckIn` 建立 `(habitId, date)` 索引,支持按日期范围查询 | 日历月查询走索引 | P0 | +| D-05 | 提供迁移 SQL 并纳入 `prisma migrate deploy` 流程 | 与 V0.1 迁移流程一致 | P0 | +| D-06 | 软删除语义:`Habit.archived`、`CheckList.archived`,历史记录保留 | 不物理删除历史 | P1 | + +### 3.7 非功能需求(N) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| N-01 | 页面首屏数据加载 < 1.5s(内网/正常网络),交互响应 < 300ms(乐观更新) | 真机可感知流畅 | P0 | +| N-02 | 兼容 Android WebView(Tauri 打包)与移动端浏览器,不依赖桌面专有 API | 真机验证 | P0 | +| N-03 | 单用户数据量按"千级记录/年"设计,无需分表;月查询一次请求返回 | 日历切月无等待感 | P1 | +| N-04 | 所有接口 `authRequired`,越权访问返回 401 | 安全基线 | P0 | +| N-05 | 前端代码组织遵循现有约定:`src/pages/checkin/*` + `src/components/*` + `server/routes/*` | 结构一致 | P0 | + +### 3.8 异常与边界需求(E) + +| 编号 | 需求描述 | 处理约定 | 优先级 | +| --- | --- | --- | --- | +| E-01 | 重复点击打卡 | 前端防抖 + 后端唯一约束,只记一次 | P0 | +| E-02 | 接口失败 / 断网 | 乐观更新回滚 + Toast 提示,可重试 | P0 | +| E-03 | **周期中途新建**打卡项(如周三创建"每周 3 次") | 本期按**完整目标 n** 计算,不按剩余天数折算 | P1 | +| E-04 | 每月 n 次在天数不同的月份 | 按**自然月**窗口,不折算 | P0 | +| E-05 | 每年 n 次跨年 | 按**自然年**窗口,年末清零 | P0 | +| E-06 | 删除打卡项 | 归档(软删除)为主;硬删除需二次确认并提示"历史记录将一并删除" | P1 | +| E-07 | 清单项全部删除后清单进度为 0/0 | 显示空清单,不显示 100% | P1 | +| E-08 | 跨零点打卡(23:59 → 00:00) | 以**提交时刻的本地日历日**归属 | P0 | + +### 3.9 功能需求 — 版本与升级(F-OT,2026-10-22 追加) + +| 编号 | 需求描述 | 验收要点 | 优先级 | +| --- | --- | --- | --- | +| F-OT-01 | **OTA 包版本必须与分支对应**:`dev-0.2` 分支上 OTA 包版本应为 `0.2.0-ota` | `GET /api/ota/check/0` 返回 `version = 0.2.0-ota` | P0 | +| F-OT-01a | **APK 版本与 OTA 版本同源**:APK(含桌面包)版本为裸版本号 `0.2.0`,无后缀;OTA 版本为同一版本号加 `-ota`。两者都跟代码分支保持一致 | APK 系统信息里 versionName=`0.2.0`(versionCode=2000),OTA manifest=`0.2.0-ota` | P0 | +| F-OT-02 | 版本号错位在 CI 阶段就拦住:①`package.json`/`tauri.conf.json`/`Cargo.toml`/`Cargo.lock` 四处必须完全一致且不含 `-ota`;②分支名 `dev-x.y` 与版本主次号不一致时构建失败 | CI 输出明确报错并中断 | P0 | +| F-OT-03 | 「我」页 OTA 组内新增**「开发版本」开关**,默认**关闭** | 关闭=按版本号比较;打开=按 build sequence 比较 | P0 | +| F-OT-04 | 开关值**持久化在设备端**,重启 App 后保持上次选择 | 杀进程重进仍为上次状态 | P0 | +| F-OT-05 | 版本号比较规则:按非数字字符切成数字字段,逐段比较大小(`0.2.0 > 0.1.0`,`0.10 > 0.9`) | 单测覆盖后缀/前缀/缺位/多位段 | P0 | +| F-OT-06 | 切换开关后清空上一次的检查结果,避免误判 | 切换即回到「未检查」状态 | P1 | + +--- + +## 4. 优先级汇总(本期交付范围) + +- **P0(必须)**:F-CK-01~08、F-CK-11;F-CL-01~08;F-LS-01~04;F-SY-01~05;F-OT-01、F-OT-01a、F-OT-02~05;U-01、U-02、U-04、U-05;D-01~05;N-01、N-02、N-04、N-05;E-01、E-02、E-04、E-05、E-08。 +- **P1(应该)**:F-CK-09、10、12~14;F-CL-09;F-LS-05~09;F-SY-06~08;F-OT-06;U-03、U-06~08;D-06;N-03;E-03、E-06、E-07。 +- **P2(可选/延后)**:F-CK-15~17;F-CL-10、11;F-LS-10、11;U-09。 + +--- + +## 5. 里程碑建议 + +| 里程碑 | 内容 | +| --- | --- | +| **M1 数据与接口** | Prisma 模型 + 迁移;`server/routes/checkin.js`、`checklist.js`;周期进度计算与日期查询 | +| **M2 打卡视图** | 环形进度组件 + 打卡卡片列表 + 打卡/撤销/补打 + 完成特效 | +| **M3 日历视图** | 月历网格 + 今天/昨天/明天三视图 + 日期标记 + 日期详情 | +| **M4 清单视图** | 清单 CRUD + 勾选交互 + 进度条 + 模板与复用 | +| **M5 联调打磨** | 真机验证、动效降级、空态/错误态、性能与跨天边界 | + +--- + +## 6. 待确认问题(评审时决策) + +| # | 问题 | 建议 | +| --- | --- | --- | +| 1 | 是否新增第 5 个底部 Tab「打卡」,还是放在首页入口? | ✅ **已决策(2026-10-22)**:「打卡」作为**默认首页放在最左**;原来的「首页」取消,报警/新闻并入宏观 | +| 2 | 周起始日是周一还是周日? | 建议周一(国内习惯) | +| 3 | 是否允许"明天提前打卡"? | 建议**不允许**(F-CK-11),保持统计口径干净 | +| 4 | 删除打卡项时历史是否保留? | 建议默认归档保留,硬删除才清历史 | +| 5 | 预置模板具体包含哪些项? | 评审时列出"出差/回老家"各 8~12 条初始项 | +| 6 | 完成特效是否可全局关闭? | ✅ **已决策(2026-10-22)**:不提供关闭入口,**特效永远打开**;页面内不再有特效开关 | + +--- + +## 附录 A:核心实体草案(供设计阶段参考,非最终表结构) + +```prisma +// 打卡项 +model Habit { + id Int @id @default(autoincrement()) + userId Int + name String + period String // day | week | month | year + target Int @default(1) // 周期内目标次数 n + color String @default("#1677ff") + icon String @default("") + note String @default("") + archived Boolean @default(false) + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + records CheckIn[] + + @@index([userId, archived]) +} + +// 打卡记录(一次打卡 = 一条;同日多次为多条) +model CheckIn { + id Int @id @default(autoincrement()) + habitId Int + habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade) + userId Int + date String // 本地日历日 "YYYY-MM-DD" (Asia/Shanghai) + seq Int @default(1) // 当日第几次 (1..n, 超额继续累加) + note String @default("") + source String @default("user") // user | backfill(补打) + createdAt DateTime @default(now()) + + @@unique([habitId, date, seq]) + @@index([userId, date]) + @@index([habitId, date]) +} + +// 检查清单 +model CheckList { + id Int @id @default(autoincrement()) + userId Int + title String + scene String @default("") // 出差 | 回老家 | 自定义 + note String @default("") + archived Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + items CheckListItem[] + + @@index([userId, archived]) +} + +// 检查项 +model CheckListItem { + id Int @id @default(autoincrement()) + listId Int + list CheckList @relation(fields: [listId], references: [id], onDelete: Cascade) + title String + note String @default("") + done Boolean @default(false) + doneAt DateTime? + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([listId, sortOrder]) +} + +// 清单模板(P1) +model CheckListTemplate { + id Int @id @default(autoincrement()) + userId Int? // null = 系统预置 + name String + scene String @default("") + items Json // [{ title, note }] + builtin Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} +``` + +> 说明:周期进度由 `CheckIn` 按周期窗口现场聚合(不冗余计数字段),避免并发与修改 n 后的一致性问题。 + +--- + +## 附录 B:实现约定(开发期补充,用于消除口径歧义) + +| # | 约定 | 说明 | +| --- | --- | --- | +| B-1 | **周期窗口** | 日=自然日;周=周一 00:00 ~ 周日 23:59;月=自然月;年=自然年;时区 Asia/Shanghai | +| B-2 | **环形进度** | 周期完成度 = 周期内累计打卡次数 / n(每周 n 次:打 1 次=1/n 环;每天 1 次:打 1 次直接闭合) | +| B-3 | **日历日期标记** | 用"当日完成度":当日次数 / **日均期望次数**(每天 n 次→n;每周 n 次→ceil(n/7);每月→ceil(n/当月天数);每年→ceil(n/365))。全完成=实心绿点,部分=黄点,超额=紫点,无记录=不显示 | +| B-4 | **streak** | 从当前周期往回数连续达标的周期数;**当前周期未结束且未达标不算断签**(跳过继续往前数) | +| B-5 | **补打限制** | 只能补打最近 7 天(`BACKFILL_DAYS`),未来日期禁止打卡 | +| B-6 | **撤销语义** | 撤销删除本周期内"最近一次"打卡(按 date desc、seq desc 取第一条) | +| B-7 | **删除打卡项** | 默认归档(`archived=true`,历史保留);`?hard=1` 才物理删除并清记录 | +| B-8 | **接口前缀** | 打卡 `/api/checkin/*`、清单 `/api/checklists/*`,全部 `authRequired`,数据按 `userId` 隔离 | +| B-9 | **前端入口** | 底部 Tab 4 个:**打卡(最左,默认选中)** / 宏观 / 监控 / 我;`/` 重定向到 `/checkin`,页内三视图:打卡 / 日历 / 清单。原「首页」取消,报警/新闻并入宏观 | +| B-10 | **特效** | 完成特效 + 触感反馈**恒开**,页面内不提供开关(2026-10-22 变更,原 `localStorage iboard_celebrate` 机制已移除) | +| B-11 | **OTA 版本** | OTA 包 `version = package.json.version + '-ota'`,必须与分支一致(`dev-0.2` → `0.2.0-ota`),CI 有强校验 | +| B-12 | **OTA 判新** | 我的页「开发版本」开关(默认关,持久化 `localStorage.iboard_ota_dev_mode`):关=版本号字段比较,开=build sequence 比较 | + +### 接口清单 + +| 方法 | 路径 | 说明 | +| --- | --- | --- | +| GET | `/api/checkin/habits?date=` | 打卡项列表 + 当前周期进度 + streak | +| POST | `/api/checkin/habits` | 新建打卡项 | +| PUT | `/api/checkin/habits/:id` | 编辑(改 n / 周期 / 归档) | +| DELETE | `/api/checkin/habits/:id?hard=1` | 归档(默认)/ 物理删除 | +| POST | `/api/checkin/habits/:id/check` | 打卡一次(可带 `date` 补打、`note`) | +| DELETE | `/api/checkin/habits/:id/check?date=` | 撤销本周期最近一次 | +| GET | `/api/checkin/day?date=` | 某日详情(今天可打卡 / 昨天可补打 / 明天只读) | +| GET | `/api/checkin/calendar?month=YYYY-MM` | 月历:每日状态 + 当月完成率 | +| GET | `/api/checklists?archived=0\|1` | 清单列表(含进度) | +| POST | `/api/checklists` | 新建清单(可带 `templateId` 从模板生成) | +| GET/PUT/DELETE | `/api/checklists/:id` | 详情 / 编辑 / 删除 | +| POST | `/api/checklists/:id/duplicate` | 复制为新清单(勾选重置) | +| POST | `/api/checklists/:id/items` | 新增检查项 | +| PUT/DELETE | `/api/checklists/items/:id` | 改标题 / 勾选(`done`) / 删除 | +| POST | `/api/checklists/:id/check-all` | 一键全选 / 全不选 | +| GET/POST | `/api/checklists/templates` | 模板列表(含系统预置)/ 另存为模板 | +| DELETE | `/api/checklists/templates/:id` | 删除自定义模板 | diff --git a/generated/prisma/browser.ts b/generated/prisma/browser.ts index 2b1f0eb..fad8cc8 100644 --- a/generated/prisma/browser.ts +++ b/generated/prisma/browser.ts @@ -107,3 +107,28 @@ export type MihomeEnvSample = Prisma.MihomeEnvSampleModel * */ export type MihomeEnvHour = Prisma.MihomeEnvHourModel +/** + * Model Habit + * + */ +export type Habit = Prisma.HabitModel +/** + * Model CheckIn + * + */ +export type CheckIn = Prisma.CheckInModel +/** + * Model CheckList + * + */ +export type CheckList = Prisma.CheckListModel +/** + * Model CheckListItem + * + */ +export type CheckListItem = Prisma.CheckListItemModel +/** + * Model CheckListTemplate + * + */ +export type CheckListTemplate = Prisma.CheckListTemplateModel diff --git a/generated/prisma/client.ts b/generated/prisma/client.ts index 5eb90db..322de4c 100644 --- a/generated/prisma/client.ts +++ b/generated/prisma/client.ts @@ -131,3 +131,28 @@ export type MihomeEnvSample = Prisma.MihomeEnvSampleModel * */ export type MihomeEnvHour = Prisma.MihomeEnvHourModel +/** + * Model Habit + * + */ +export type Habit = Prisma.HabitModel +/** + * Model CheckIn + * + */ +export type CheckIn = Prisma.CheckInModel +/** + * Model CheckList + * + */ +export type CheckList = Prisma.CheckListModel +/** + * Model CheckListItem + * + */ +export type CheckListItem = Prisma.CheckListItemModel +/** + * Model CheckListTemplate + * + */ +export type CheckListTemplate = Prisma.CheckListTemplateModel diff --git a/generated/prisma/internal/class.ts b/generated/prisma/internal/class.ts index 9d85193..c8ce4ba 100644 --- a/generated/prisma/internal/class.ts +++ b/generated/prisma/internal/class.ts @@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = { "clientVersion": "7.7.0", "engineVersion": "75cbdc1eb7150937890ad5465d861175c6624711", "activeProvider": "mysql", - "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Get a free hosted Postgres database in seconds: `npx create-db`\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"mysql\"\n}\n\nmodel User {\n id Int @id @default(autoincrement())\n name String\n email String @unique\n password String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Article {\n id Int @id @default(autoincrement())\n title String\n slug String @unique\n content String\n date String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Strategy {\n id Int @id @default(autoincrement())\n name String\n description String\n data Json\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Indicator {\n id Int @id @default(autoincrement())\n name String @unique\n groupName String\n title String @default(\"\")\n unit String @default(\"\")\n source String @default(\"\")\n frequency String @default(\"month\") // year | quarter | month | day\n hidden Boolean @default(false) // true = 数据/脚本保留, 但不在前端展示\n points IndicatorPoint[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel IndicatorPoint {\n id Int @id @default(autoincrement())\n indicatorId Int\n indicator Indicator @relation(fields: [indicatorId], references: [id], onDelete: Cascade)\n period String // \"2024\" | \"2024-Q1\" | \"2024-01\" | \"2024-01-15\"\n value Float\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([indicatorId, period])\n}\n\n// 新闻: 每天 3 点由 mx_finance_search_news 拉取, 按影响大小排序, 累加多日供首页无限滚动\nmodel News {\n id Int @id @default(autoincrement())\n title String\n summary String @default(\"\")\n content String? @db.Text\n source String @default(\"\")\n url String @default(\"\")\n publishedAt DateTime\n impactRank Int\n createdAt DateTime @default(now())\n\n @@unique([title, publishedAt])\n @@index([publishedAt])\n}\n\n// 报警: refresh 写库时比对同 (indicatorName, period) 的跨源数据, 差异超阈值记录\nmodel Alert {\n id Int @id @default(autoincrement())\n indicatorName String\n period String\n valueA Float\n valueB Float\n sourceA String\n sourceB String\n diff Float\n diffRatio Float\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\n// 服务器监控: 对接 komari 公开 API\nmodel ServerNode {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n cpuName String @default(\"\")\n os String @default(\"\")\n region String @default(\"\")\n memTotal BigInt @default(0)\n diskTotal BigInt @default(0)\n cpuCores Int @default(0)\n // 最新实时快照\n cpuUsage Float @default(0)\n ramUsed BigInt @default(0)\n swapUsed BigInt @default(0)\n load1 Float @default(0)\n load5 Float @default(0)\n load15 Float @default(0)\n diskUsed BigInt @default(0)\n netIn BigInt @default(0)\n netOut BigInt @default(0)\n uptime BigInt @default(0)\n process Int @default(0)\n connections Int @default(0)\n status String @default(\"unknown\") // online | offline | unknown\n recordedAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// 时序历史: 每节点每分钟一条 (komari records 1 分钟粒度)\nmodel ServerNodeHistory {\n id Int @id @default(autoincrement())\n uuid String\n period DateTime\n cpu Float @default(0)\n ram BigInt @default(0)\n load Float @default(0)\n disk BigInt @default(0)\n netIn BigInt @default(0)\n netOut BigInt @default(0)\n process Int @default(0)\n connections Int @default(0)\n createdAt DateTime @default(now())\n\n @@unique([uuid, period])\n @@index([uuid, period])\n}\n\n// 服务健康检查: 本应用自检 (健康端点 / DB / 第三方)\nmodel ServiceCheck {\n id Int @id @default(autoincrement())\n name String @unique\n url String\n status String @default(\"unknown\") // up | down | unknown\n latencyMs Int @default(0)\n message String @default(\"\")\n lastCheckAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Econ_SowInventory {\n id Int @id @default(autoincrement())\n month String @unique\n inventory Int // 能繁母猪数量(万头)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ScraperCache {\n id Int @id @default(autoincrement())\n scraperName String @unique // 爬虫名称,如:sow-inventory\n lastDataHash String? // 上次数据指纹\n lastUpdateAt DateTime // 上次更新时间\n metadata Json? // 其他元数据\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// 浙江省油价历史 (apizero price 接口每日抓取)\n// 每天 04:00 cron 触发: 检查 schedule 里待定且 <= 昨天的调价日, 抓 price?province=浙江 存本表\n// unique (date, province, fuel) 防重复\nmodel OilPriceHistory {\n id Int @id @default(autoincrement())\n date DateTime // 调价日次日 (即数据实际抓取日)\n province String // '浙江'\n fuel String // '92' | '95' | '0'\n price Float // 油价 (元/升, 4 位小数足够)\n source String @default(\"apizero\")\n createdAt DateTime @default(now())\n\n @@unique([date, province, fuel])\n @@index([province, fuel, date])\n}\n\n// ==================== 环境监控:米家 ====================\n\n// 米家授权状态 (DiskCache 存凭证, 本表只存状态/错误供前端展示)\nmodel MihomeAccount {\n id Int @id @default(autoincrement())\n key String @unique @default(\"default\")\n userId String @default(\"\")\n status String @default(\"unknown\") // ok | pending | expired\n lastError String @default(\"\")\n updatedAt DateTime @updatedAt\n}\n\nmodel MihomeHome {\n id Int @id @default(autoincrement())\n homeId String @unique\n name String\n createdAt DateTime @default(now())\n devices MihomeDevice[]\n}\n\nmodel MihomeDevice {\n id Int @id @default(autoincrement())\n did String @unique\n homeId String\n home MihomeHome @relation(fields: [homeId], references: [homeId])\n model String\n name String\n alias String? // 用户别名, 仅本应用展示\n tempSiid Int\n tempPiid Int\n humSiid Int\n humPiid Int\n battSiid Int // 电池电量属性 (炎米 3.1003 / 青萍 2.3)\n battPiid Int\n battery Int? // 最近一次电量 (每天读一次, 标题展示用)\n batteryAt DateTime? // 电量读取时间 (当天缓存, 不频繁更新)\n enabled Boolean @default(true)\n lastSyncAt DateTime?\n createdAt DateTime @default(now())\n samples MihomeEnvSample[]\n hours MihomeEnvHour[]\n}\n\n// 日档: 月视图(逐日)数据源; unique (deviceId, date) 防重\nmodel MihomeEnvSample {\n id Int @id @default(autoincrement())\n deviceId Int\n device MihomeDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n date DateTime // 采样日 (Asia/Shanghai 当日 00:00 存 UTC)\n temperature Float? // 当日最后一次实时值 (或 vendor hour 均值)\n humidity Float?\n temperatureAvg Float? // 当日均值 (由小时档聚合, 或 vendor_hour)\n humidityAvg Float?\n temperatureMin Float? // 当日最低\n temperatureMax Float? // 当日最高\n humidityMin Float?\n humidityMax Float?\n origin String @default(\"live\") // live | vendor_history | backfill\n sampledAt DateTime?\n raw Json?\n createdAt DateTime @default(now())\n\n @@unique([deviceId, date])\n @@index([deviceId, date])\n}\n\n// 小时档: 当天视图(逐小时)数据源; unique (deviceId, hour) 防重\nmodel MihomeEnvHour {\n id Int @id @default(autoincrement())\n deviceId Int\n device MihomeDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n hour DateTime // Asia/Shanghai 整点 (存 UTC)\n temperatureAvg Float? // 该小时均值 (live 采样点, 或 vendor_hour)\n humidityAvg Float?\n raw Json?\n createdAt DateTime @default(now())\n\n @@unique([deviceId, hour])\n @@index([deviceId, hour])\n}\n", + "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Get a free hosted Postgres database in seconds: `npx create-db`\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"mysql\"\n}\n\nmodel User {\n id Int @id @default(autoincrement())\n name String\n email String @unique\n password String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // V0.2 打卡 / 清单: 用户删除时级联清理\n habits Habit[]\n checkIns CheckIn[]\n checkLists CheckList[]\n templates CheckListTemplate[]\n}\n\nmodel Article {\n id Int @id @default(autoincrement())\n title String\n slug String @unique\n content String\n date String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Strategy {\n id Int @id @default(autoincrement())\n name String\n description String\n data Json\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Indicator {\n id Int @id @default(autoincrement())\n name String @unique\n groupName String\n title String @default(\"\")\n unit String @default(\"\")\n source String @default(\"\")\n frequency String @default(\"month\") // year | quarter | month | day\n hidden Boolean @default(false) // true = 数据/脚本保留, 但不在前端展示\n points IndicatorPoint[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel IndicatorPoint {\n id Int @id @default(autoincrement())\n indicatorId Int\n indicator Indicator @relation(fields: [indicatorId], references: [id], onDelete: Cascade)\n period String // \"2024\" | \"2024-Q1\" | \"2024-01\" | \"2024-01-15\"\n value Float\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([indicatorId, period])\n}\n\n// 新闻: 每天 3 点由 mx_finance_search_news 拉取, 按影响大小排序, 累加多日供首页无限滚动\nmodel News {\n id Int @id @default(autoincrement())\n title String\n summary String @default(\"\")\n content String? @db.Text\n source String @default(\"\")\n url String @default(\"\")\n publishedAt DateTime\n impactRank Int\n createdAt DateTime @default(now())\n\n @@unique([title, publishedAt])\n @@index([publishedAt])\n}\n\n// 报警: refresh 写库时比对同 (indicatorName, period) 的跨源数据, 差异超阈值记录\nmodel Alert {\n id Int @id @default(autoincrement())\n indicatorName String\n period String\n valueA Float\n valueB Float\n sourceA String\n sourceB String\n diff Float\n diffRatio Float\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\n// 服务器监控: 对接 komari 公开 API\nmodel ServerNode {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n cpuName String @default(\"\")\n os String @default(\"\")\n region String @default(\"\")\n memTotal BigInt @default(0)\n diskTotal BigInt @default(0)\n cpuCores Int @default(0)\n // 最新实时快照\n cpuUsage Float @default(0)\n ramUsed BigInt @default(0)\n swapUsed BigInt @default(0)\n load1 Float @default(0)\n load5 Float @default(0)\n load15 Float @default(0)\n diskUsed BigInt @default(0)\n netIn BigInt @default(0)\n netOut BigInt @default(0)\n uptime BigInt @default(0)\n process Int @default(0)\n connections Int @default(0)\n status String @default(\"unknown\") // online | offline | unknown\n recordedAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// 时序历史: 每节点每分钟一条 (komari records 1 分钟粒度)\nmodel ServerNodeHistory {\n id Int @id @default(autoincrement())\n uuid String\n period DateTime\n cpu Float @default(0)\n ram BigInt @default(0)\n load Float @default(0)\n disk BigInt @default(0)\n netIn BigInt @default(0)\n netOut BigInt @default(0)\n process Int @default(0)\n connections Int @default(0)\n createdAt DateTime @default(now())\n\n @@unique([uuid, period])\n @@index([uuid, period])\n}\n\n// 服务健康检查: 本应用自检 (健康端点 / DB / 第三方)\nmodel ServiceCheck {\n id Int @id @default(autoincrement())\n name String @unique\n url String\n status String @default(\"unknown\") // up | down | unknown\n latencyMs Int @default(0)\n message String @default(\"\")\n lastCheckAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Econ_SowInventory {\n id Int @id @default(autoincrement())\n month String @unique\n inventory Int // 能繁母猪数量(万头)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ScraperCache {\n id Int @id @default(autoincrement())\n scraperName String @unique // 爬虫名称,如:sow-inventory\n lastDataHash String? // 上次数据指纹\n lastUpdateAt DateTime // 上次更新时间\n metadata Json? // 其他元数据\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// 浙江省油价历史 (apizero price 接口每日抓取)\n// 每天 04:00 cron 触发: 检查 schedule 里待定且 <= 昨天的调价日, 抓 price?province=浙江 存本表\n// unique (date, province, fuel) 防重复\nmodel OilPriceHistory {\n id Int @id @default(autoincrement())\n date DateTime // 调价日次日 (即数据实际抓取日)\n province String // '浙江'\n fuel String // '92' | '95' | '0'\n price Float // 油价 (元/升, 4 位小数足够)\n source String @default(\"apizero\")\n createdAt DateTime @default(now())\n\n @@unique([date, province, fuel])\n @@index([province, fuel, date])\n}\n\n// ==================== 环境监控:米家 ====================\n\n// 米家授权状态 (DiskCache 存凭证, 本表只存状态/错误供前端展示)\nmodel MihomeAccount {\n id Int @id @default(autoincrement())\n key String @unique @default(\"default\")\n userId String @default(\"\")\n status String @default(\"unknown\") // ok | pending | expired\n lastError String @default(\"\")\n updatedAt DateTime @updatedAt\n}\n\nmodel MihomeHome {\n id Int @id @default(autoincrement())\n homeId String @unique\n name String\n createdAt DateTime @default(now())\n devices MihomeDevice[]\n}\n\nmodel MihomeDevice {\n id Int @id @default(autoincrement())\n did String @unique\n homeId String\n home MihomeHome @relation(fields: [homeId], references: [homeId])\n model String\n name String\n alias String? // 用户别名, 仅本应用展示\n tempSiid Int\n tempPiid Int\n humSiid Int\n humPiid Int\n battSiid Int // 电池电量属性 (炎米 3.1003 / 青萍 2.3)\n battPiid Int\n battery Int? // 最近一次电量 (每天读一次, 标题展示用)\n batteryAt DateTime? // 电量读取时间 (当天缓存, 不频繁更新)\n enabled Boolean @default(true)\n lastSyncAt DateTime?\n createdAt DateTime @default(now())\n samples MihomeEnvSample[]\n hours MihomeEnvHour[]\n}\n\n// 日档: 月视图(逐日)数据源; unique (deviceId, date) 防重\nmodel MihomeEnvSample {\n id Int @id @default(autoincrement())\n deviceId Int\n device MihomeDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n date DateTime // 采样日 (Asia/Shanghai 当日 00:00 存 UTC)\n temperature Float? // 当日最后一次实时值 (或 vendor hour 均值)\n humidity Float?\n temperatureAvg Float? // 当日均值 (由小时档聚合, 或 vendor_hour)\n humidityAvg Float?\n temperatureMin Float? // 当日最低\n temperatureMax Float? // 当日最高\n humidityMin Float?\n humidityMax Float?\n origin String @default(\"live\") // live | vendor_history | backfill\n sampledAt DateTime?\n raw Json?\n createdAt DateTime @default(now())\n\n @@unique([deviceId, date])\n @@index([deviceId, date])\n}\n\n// 小时档: 当天视图(逐小时)数据源; unique (deviceId, hour) 防重\nmodel MihomeEnvHour {\n id Int @id @default(autoincrement())\n deviceId Int\n device MihomeDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n hour DateTime // Asia/Shanghai 整点 (存 UTC)\n temperatureAvg Float? // 该小时均值 (live 采样点, 或 vendor_hour)\n humidityAvg Float?\n raw Json?\n createdAt DateTime @default(now())\n\n @@unique([deviceId, hour])\n @@index([deviceId, hour])\n}\n\n// ==================== 打卡 / 日历 / 清单 (V0.2) ====================\n\n// 打卡项: 周期性目标 (每天 / 每周 / 每月 / 每年 n 次)\nmodel Habit {\n id Int @id @default(autoincrement())\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String\n period String @default(\"day\") // day | week | month | year\n target Int @default(1) // 周期内目标次数 n (>=1)\n color String @default(\"#1677ff\")\n icon String @default(\"\") // emoji 或图标名, 预留\n note String @default(\"\")\n archived Boolean @default(false) // 归档: 不出现在今日待办, 历史保留\n sortOrder Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n records CheckIn[]\n\n @@index([userId, archived, sortOrder])\n}\n\n// 打卡记录: 一次打卡 = 一条; 同日多次 => seq 递增 (1..n, 超额继续累加)\nmodel CheckIn {\n id Int @id @default(autoincrement())\n habitId Int\n habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade)\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n date String @db.VarChar(10) // 本地日历日 \"YYYY-MM-DD\" (Asia/Shanghai)\n seq Int @default(1) // 当日第几次\n note String @default(\"\")\n source String @default(\"user\") // user | backfill (补打)\n createdAt DateTime @default(now())\n\n @@unique([habitId, date, seq])\n @@index([userId, date])\n @@index([habitId, date])\n}\n\n// 检查清单: 出差 / 回老家 等场景的一次性清单\nmodel CheckList {\n id Int @id @default(autoincrement())\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n title String\n scene String @default(\"\") // 出差 | 回老家 | 自定义\n note String @default(\"\")\n archived Boolean @default(false)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n items CheckListItem[]\n\n @@index([userId, archived])\n}\n\n// 检查项: 勾选即完成\nmodel CheckListItem {\n id Int @id @default(autoincrement())\n listId Int\n list CheckList @relation(fields: [listId], references: [id], onDelete: Cascade)\n title String\n note String @default(\"\")\n done Boolean @default(false)\n doneAt DateTime?\n sortOrder Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([listId, sortOrder])\n}\n\n// 清单模板: builtin = 系统预置 (userId 为空); 否则为用户自定义\nmodel CheckListTemplate {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String\n scene String @default(\"\")\n items Json // [{ title, note }]\n builtin Boolean @default(false)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId, builtin])\n}\n", "runtimeDataModel": { "models": {}, "enums": {}, @@ -32,10 +32,10 @@ const config: runtime.GetPrismaClientConfig = { } } -config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Article\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"slug\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Strategy\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Indicator\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"groupName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"unit\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"frequency\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hidden\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"points\",\"kind\":\"object\",\"type\":\"IndicatorPoint\",\"relationName\":\"IndicatorToIndicatorPoint\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"IndicatorPoint\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicatorId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicator\",\"kind\":\"object\",\"type\":\"Indicator\",\"relationName\":\"IndicatorToIndicatorPoint\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"News\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"summary\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"url\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"publishedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"impactRank\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Alert\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicatorName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"valueA\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"valueB\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"sourceA\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sourceB\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"diff\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"diffRatio\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServerNode\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uuid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cpuName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"os\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"region\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"memTotal\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"diskTotal\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"cpuCores\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cpuUsage\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"ramUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"swapUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"load1\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"load5\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"load15\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"diskUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netIn\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netOut\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"uptime\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"process\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connections\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"recordedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServerNodeHistory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uuid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"cpu\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"ram\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"load\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"disk\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netIn\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netOut\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"process\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connections\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceCheck\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"url\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"latencyMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"message\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastCheckAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Econ_SowInventory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"month\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inventory\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScraperCache\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scraperName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastDataHash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastUpdateAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"OilPriceHistory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"province\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fuel\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeAccount\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastError\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeHome\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"homeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"devices\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeHome\"}],\"dbName\":null},\"MihomeDevice\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"did\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"homeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"home\",\"kind\":\"object\",\"type\":\"MihomeHome\",\"relationName\":\"MihomeDeviceToMihomeHome\"},{\"name\":\"model\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"alias\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tempSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"tempPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"humSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"humPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battery\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"batteryAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"enabled\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"lastSyncAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"samples\",\"kind\":\"object\",\"type\":\"MihomeEnvSample\",\"relationName\":\"MihomeDeviceToMihomeEnvSample\"},{\"name\":\"hours\",\"kind\":\"object\",\"type\":\"MihomeEnvHour\",\"relationName\":\"MihomeDeviceToMihomeEnvHour\"}],\"dbName\":null},\"MihomeEnvSample\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeEnvSample\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"temperature\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidity\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureMin\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureMax\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityMin\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityMax\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"origin\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sampledAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"raw\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeEnvHour\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeEnvHour\"},{\"name\":\"hour\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"temperatureAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"raw\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"habits\",\"kind\":\"object\",\"type\":\"Habit\",\"relationName\":\"HabitToUser\"},{\"name\":\"checkIns\",\"kind\":\"object\",\"type\":\"CheckIn\",\"relationName\":\"CheckInToUser\"},{\"name\":\"checkLists\",\"kind\":\"object\",\"type\":\"CheckList\",\"relationName\":\"CheckListToUser\"},{\"name\":\"templates\",\"kind\":\"object\",\"type\":\"CheckListTemplate\",\"relationName\":\"CheckListTemplateToUser\"}],\"dbName\":null},\"Article\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"slug\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Strategy\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Indicator\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"groupName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"unit\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"frequency\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hidden\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"points\",\"kind\":\"object\",\"type\":\"IndicatorPoint\",\"relationName\":\"IndicatorToIndicatorPoint\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"IndicatorPoint\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicatorId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicator\",\"kind\":\"object\",\"type\":\"Indicator\",\"relationName\":\"IndicatorToIndicatorPoint\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"News\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"summary\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"url\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"publishedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"impactRank\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Alert\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"indicatorName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"valueA\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"valueB\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"sourceA\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sourceB\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"diff\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"diffRatio\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServerNode\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uuid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cpuName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"os\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"region\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"memTotal\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"diskTotal\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"cpuCores\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cpuUsage\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"ramUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"swapUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"load1\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"load5\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"load15\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"diskUsed\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netIn\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netOut\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"uptime\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"process\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connections\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"recordedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServerNodeHistory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uuid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"cpu\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"ram\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"load\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"disk\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netIn\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"netOut\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"process\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connections\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceCheck\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"url\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"latencyMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"message\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastCheckAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Econ_SowInventory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"month\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inventory\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScraperCache\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scraperName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastDataHash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastUpdateAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"OilPriceHistory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"province\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fuel\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeAccount\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastError\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeHome\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"homeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"devices\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeHome\"}],\"dbName\":null},\"MihomeDevice\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"did\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"homeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"home\",\"kind\":\"object\",\"type\":\"MihomeHome\",\"relationName\":\"MihomeDeviceToMihomeHome\"},{\"name\":\"model\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"alias\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tempSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"tempPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"humSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"humPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battSiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battPiid\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"battery\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"batteryAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"enabled\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"lastSyncAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"samples\",\"kind\":\"object\",\"type\":\"MihomeEnvSample\",\"relationName\":\"MihomeDeviceToMihomeEnvSample\"},{\"name\":\"hours\",\"kind\":\"object\",\"type\":\"MihomeEnvHour\",\"relationName\":\"MihomeDeviceToMihomeEnvHour\"}],\"dbName\":null},\"MihomeEnvSample\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeEnvSample\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"temperature\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidity\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureMin\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"temperatureMax\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityMin\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityMax\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"origin\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sampledAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"raw\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MihomeEnvHour\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"MihomeDevice\",\"relationName\":\"MihomeDeviceToMihomeEnvHour\"},{\"name\":\"hour\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"temperatureAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"humidityAvg\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"raw\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Habit\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"HabitToUser\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"period\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"target\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"color\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"icon\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"note\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"archived\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"records\",\"kind\":\"object\",\"type\":\"CheckIn\",\"relationName\":\"CheckInToHabit\"}],\"dbName\":null},\"CheckIn\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"habitId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"habit\",\"kind\":\"object\",\"type\":\"Habit\",\"relationName\":\"CheckInToHabit\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CheckInToUser\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"seq\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"note\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CheckList\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CheckListToUser\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"scene\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"note\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"archived\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"items\",\"kind\":\"object\",\"type\":\"CheckListItem\",\"relationName\":\"CheckListToCheckListItem\"}],\"dbName\":null},\"CheckListItem\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"listId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"list\",\"kind\":\"object\",\"type\":\"CheckList\",\"relationName\":\"CheckListToCheckListItem\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"note\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"done\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"doneAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CheckListTemplate\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CheckListTemplateToUser\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"scene\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"items\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"builtin\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") config.parameterizationSchema = { - strings: JSON.parse("[\"where\",\"User.findUnique\",\"User.findUniqueOrThrow\",\"orderBy\",\"cursor\",\"User.findFirst\",\"User.findFirstOrThrow\",\"User.findMany\",\"data\",\"User.createOne\",\"User.createMany\",\"User.updateOne\",\"User.updateMany\",\"create\",\"update\",\"User.upsertOne\",\"User.deleteOne\",\"User.deleteMany\",\"having\",\"_count\",\"_avg\",\"_sum\",\"_min\",\"_max\",\"User.groupBy\",\"User.aggregate\",\"Article.findUnique\",\"Article.findUniqueOrThrow\",\"Article.findFirst\",\"Article.findFirstOrThrow\",\"Article.findMany\",\"Article.createOne\",\"Article.createMany\",\"Article.updateOne\",\"Article.updateMany\",\"Article.upsertOne\",\"Article.deleteOne\",\"Article.deleteMany\",\"Article.groupBy\",\"Article.aggregate\",\"Strategy.findUnique\",\"Strategy.findUniqueOrThrow\",\"Strategy.findFirst\",\"Strategy.findFirstOrThrow\",\"Strategy.findMany\",\"Strategy.createOne\",\"Strategy.createMany\",\"Strategy.updateOne\",\"Strategy.updateMany\",\"Strategy.upsertOne\",\"Strategy.deleteOne\",\"Strategy.deleteMany\",\"Strategy.groupBy\",\"Strategy.aggregate\",\"indicator\",\"points\",\"Indicator.findUnique\",\"Indicator.findUniqueOrThrow\",\"Indicator.findFirst\",\"Indicator.findFirstOrThrow\",\"Indicator.findMany\",\"Indicator.createOne\",\"Indicator.createMany\",\"Indicator.updateOne\",\"Indicator.updateMany\",\"Indicator.upsertOne\",\"Indicator.deleteOne\",\"Indicator.deleteMany\",\"Indicator.groupBy\",\"Indicator.aggregate\",\"IndicatorPoint.findUnique\",\"IndicatorPoint.findUniqueOrThrow\",\"IndicatorPoint.findFirst\",\"IndicatorPoint.findFirstOrThrow\",\"IndicatorPoint.findMany\",\"IndicatorPoint.createOne\",\"IndicatorPoint.createMany\",\"IndicatorPoint.updateOne\",\"IndicatorPoint.updateMany\",\"IndicatorPoint.upsertOne\",\"IndicatorPoint.deleteOne\",\"IndicatorPoint.deleteMany\",\"IndicatorPoint.groupBy\",\"IndicatorPoint.aggregate\",\"News.findUnique\",\"News.findUniqueOrThrow\",\"News.findFirst\",\"News.findFirstOrThrow\",\"News.findMany\",\"News.createOne\",\"News.createMany\",\"News.updateOne\",\"News.updateMany\",\"News.upsertOne\",\"News.deleteOne\",\"News.deleteMany\",\"News.groupBy\",\"News.aggregate\",\"Alert.findUnique\",\"Alert.findUniqueOrThrow\",\"Alert.findFirst\",\"Alert.findFirstOrThrow\",\"Alert.findMany\",\"Alert.createOne\",\"Alert.createMany\",\"Alert.updateOne\",\"Alert.updateMany\",\"Alert.upsertOne\",\"Alert.deleteOne\",\"Alert.deleteMany\",\"Alert.groupBy\",\"Alert.aggregate\",\"ServerNode.findUnique\",\"ServerNode.findUniqueOrThrow\",\"ServerNode.findFirst\",\"ServerNode.findFirstOrThrow\",\"ServerNode.findMany\",\"ServerNode.createOne\",\"ServerNode.createMany\",\"ServerNode.updateOne\",\"ServerNode.updateMany\",\"ServerNode.upsertOne\",\"ServerNode.deleteOne\",\"ServerNode.deleteMany\",\"ServerNode.groupBy\",\"ServerNode.aggregate\",\"ServerNodeHistory.findUnique\",\"ServerNodeHistory.findUniqueOrThrow\",\"ServerNodeHistory.findFirst\",\"ServerNodeHistory.findFirstOrThrow\",\"ServerNodeHistory.findMany\",\"ServerNodeHistory.createOne\",\"ServerNodeHistory.createMany\",\"ServerNodeHistory.updateOne\",\"ServerNodeHistory.updateMany\",\"ServerNodeHistory.upsertOne\",\"ServerNodeHistory.deleteOne\",\"ServerNodeHistory.deleteMany\",\"ServerNodeHistory.groupBy\",\"ServerNodeHistory.aggregate\",\"ServiceCheck.findUnique\",\"ServiceCheck.findUniqueOrThrow\",\"ServiceCheck.findFirst\",\"ServiceCheck.findFirstOrThrow\",\"ServiceCheck.findMany\",\"ServiceCheck.createOne\",\"ServiceCheck.createMany\",\"ServiceCheck.updateOne\",\"ServiceCheck.updateMany\",\"ServiceCheck.upsertOne\",\"ServiceCheck.deleteOne\",\"ServiceCheck.deleteMany\",\"ServiceCheck.groupBy\",\"ServiceCheck.aggregate\",\"Econ_SowInventory.findUnique\",\"Econ_SowInventory.findUniqueOrThrow\",\"Econ_SowInventory.findFirst\",\"Econ_SowInventory.findFirstOrThrow\",\"Econ_SowInventory.findMany\",\"Econ_SowInventory.createOne\",\"Econ_SowInventory.createMany\",\"Econ_SowInventory.updateOne\",\"Econ_SowInventory.updateMany\",\"Econ_SowInventory.upsertOne\",\"Econ_SowInventory.deleteOne\",\"Econ_SowInventory.deleteMany\",\"Econ_SowInventory.groupBy\",\"Econ_SowInventory.aggregate\",\"ScraperCache.findUnique\",\"ScraperCache.findUniqueOrThrow\",\"ScraperCache.findFirst\",\"ScraperCache.findFirstOrThrow\",\"ScraperCache.findMany\",\"ScraperCache.createOne\",\"ScraperCache.createMany\",\"ScraperCache.updateOne\",\"ScraperCache.updateMany\",\"ScraperCache.upsertOne\",\"ScraperCache.deleteOne\",\"ScraperCache.deleteMany\",\"ScraperCache.groupBy\",\"ScraperCache.aggregate\",\"OilPriceHistory.findUnique\",\"OilPriceHistory.findUniqueOrThrow\",\"OilPriceHistory.findFirst\",\"OilPriceHistory.findFirstOrThrow\",\"OilPriceHistory.findMany\",\"OilPriceHistory.createOne\",\"OilPriceHistory.createMany\",\"OilPriceHistory.updateOne\",\"OilPriceHistory.updateMany\",\"OilPriceHistory.upsertOne\",\"OilPriceHistory.deleteOne\",\"OilPriceHistory.deleteMany\",\"OilPriceHistory.groupBy\",\"OilPriceHistory.aggregate\",\"MihomeAccount.findUnique\",\"MihomeAccount.findUniqueOrThrow\",\"MihomeAccount.findFirst\",\"MihomeAccount.findFirstOrThrow\",\"MihomeAccount.findMany\",\"MihomeAccount.createOne\",\"MihomeAccount.createMany\",\"MihomeAccount.updateOne\",\"MihomeAccount.updateMany\",\"MihomeAccount.upsertOne\",\"MihomeAccount.deleteOne\",\"MihomeAccount.deleteMany\",\"MihomeAccount.groupBy\",\"MihomeAccount.aggregate\",\"home\",\"device\",\"samples\",\"hours\",\"devices\",\"MihomeHome.findUnique\",\"MihomeHome.findUniqueOrThrow\",\"MihomeHome.findFirst\",\"MihomeHome.findFirstOrThrow\",\"MihomeHome.findMany\",\"MihomeHome.createOne\",\"MihomeHome.createMany\",\"MihomeHome.updateOne\",\"MihomeHome.updateMany\",\"MihomeHome.upsertOne\",\"MihomeHome.deleteOne\",\"MihomeHome.deleteMany\",\"MihomeHome.groupBy\",\"MihomeHome.aggregate\",\"MihomeDevice.findUnique\",\"MihomeDevice.findUniqueOrThrow\",\"MihomeDevice.findFirst\",\"MihomeDevice.findFirstOrThrow\",\"MihomeDevice.findMany\",\"MihomeDevice.createOne\",\"MihomeDevice.createMany\",\"MihomeDevice.updateOne\",\"MihomeDevice.updateMany\",\"MihomeDevice.upsertOne\",\"MihomeDevice.deleteOne\",\"MihomeDevice.deleteMany\",\"MihomeDevice.groupBy\",\"MihomeDevice.aggregate\",\"MihomeEnvSample.findUnique\",\"MihomeEnvSample.findUniqueOrThrow\",\"MihomeEnvSample.findFirst\",\"MihomeEnvSample.findFirstOrThrow\",\"MihomeEnvSample.findMany\",\"MihomeEnvSample.createOne\",\"MihomeEnvSample.createMany\",\"MihomeEnvSample.updateOne\",\"MihomeEnvSample.updateMany\",\"MihomeEnvSample.upsertOne\",\"MihomeEnvSample.deleteOne\",\"MihomeEnvSample.deleteMany\",\"MihomeEnvSample.groupBy\",\"MihomeEnvSample.aggregate\",\"MihomeEnvHour.findUnique\",\"MihomeEnvHour.findUniqueOrThrow\",\"MihomeEnvHour.findFirst\",\"MihomeEnvHour.findFirstOrThrow\",\"MihomeEnvHour.findMany\",\"MihomeEnvHour.createOne\",\"MihomeEnvHour.createMany\",\"MihomeEnvHour.updateOne\",\"MihomeEnvHour.updateMany\",\"MihomeEnvHour.upsertOne\",\"MihomeEnvHour.deleteOne\",\"MihomeEnvHour.deleteMany\",\"MihomeEnvHour.groupBy\",\"MihomeEnvHour.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"deviceId\",\"hour\",\"temperatureAvg\",\"humidityAvg\",\"raw\",\"createdAt\",\"equals\",\"string_contains\",\"string_starts_with\",\"string_ends_with\",\"array_starts_with\",\"array_ends_with\",\"array_contains\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"in\",\"notIn\",\"date\",\"temperature\",\"humidity\",\"temperatureMin\",\"temperatureMax\",\"humidityMin\",\"humidityMax\",\"origin\",\"sampledAt\",\"contains\",\"startsWith\",\"endsWith\",\"search\",\"did\",\"homeId\",\"model\",\"name\",\"alias\",\"tempSiid\",\"tempPiid\",\"humSiid\",\"humPiid\",\"battSiid\",\"battPiid\",\"battery\",\"batteryAt\",\"enabled\",\"lastSyncAt\",\"every\",\"some\",\"none\",\"deviceId_hour\",\"deviceId_date\",\"key\",\"userId\",\"status\",\"lastError\",\"updatedAt\",\"province\",\"fuel\",\"price\",\"source\",\"date_province_fuel\",\"scraperName\",\"lastDataHash\",\"lastUpdateAt\",\"metadata\",\"month\",\"inventory\",\"url\",\"latencyMs\",\"message\",\"lastCheckAt\",\"uuid\",\"period\",\"cpu\",\"ram\",\"load\",\"disk\",\"netIn\",\"netOut\",\"process\",\"connections\",\"uuid_period\",\"cpuName\",\"os\",\"region\",\"memTotal\",\"diskTotal\",\"cpuCores\",\"cpuUsage\",\"ramUsed\",\"swapUsed\",\"load1\",\"load5\",\"load15\",\"diskUsed\",\"uptime\",\"recordedAt\",\"indicatorName\",\"valueA\",\"valueB\",\"sourceA\",\"sourceB\",\"diff\",\"diffRatio\",\"title\",\"summary\",\"content\",\"publishedAt\",\"impactRank\",\"title_publishedAt\",\"indicatorId\",\"value\",\"groupName\",\"unit\",\"frequency\",\"hidden\",\"indicatorId_period\",\"description\",\"slug\",\"email\",\"password\",\"is\",\"isNot\",\"connectOrCreate\",\"upsert\",\"createMany\",\"set\",\"disconnect\",\"delete\",\"connect\",\"updateMany\",\"deleteMany\",\"_relevance\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"), - graph: "2gWUAfwBCY8CAACFBAAwkAIAAAQAEJECAACFBAAwkgICAAAAAZgCQADJAwAhtwIBAMgDACHMAkAAyQMAIYwDAQAAAAGNAwEAyAMAIQEAAAABACABAAAAAQAgCY8CAACFBAAwkAIAAAQAEJECAACFBAAwkgICAMcDACGYAkAAyQMAIbcCAQDIAwAhzAJAAMkDACGMAwEAyAMAIY0DAQDIAwAhAZkDAADCBQAgAwAAAAQAIAMAAAUAMAQAAAEAIAMAAAAEACADAAAFADAEAAABACADAAAABAAgAwAABQAwBAAAAQAgBpICAgAAAAGYAkAAAAABtwIBAAAAAcwCQAAAAAGMAwEAAAABjQMBAAAAAQEIAAAJACAGkgICAAAAAZgCQAAAAAG3AgEAAAABzAJAAAAAAYwDAQAAAAGNAwEAAAABAQgAAAsAMAaSAgIAjgQAIZgCQACMBAAhtwIBAJYEACHMAkAAjAQAIYwDAQCWBAAhjQMBAJYEACECAAAAAQAgCAAADQAgBpICAgCOBAAhmAJAAIwEACG3AgEAlgQAIcwCQACMBAAhjAMBAJYEACGNAwEAlgQAIQIAAAAEACAIAAAPACADAAAAAQAgDQAACQAgDgAADQAgAQAAAAEAIAEAAAAEACAFEwAAvQUAIBQAAL4FACAVAADBBQAgFgAAwAUAIBcAAL8FACAJjwIAAIQEADCQAgAAFQAQkQIAAIQEADCSAgIAqAMAIZgCQACpAwAhtwIBALYDACHMAkAAqQMAIYwDAQC2AwAhjQMBALYDACEDAAAABAAgAwAAFAAwEgAAFQAgAwAAAAQAIAMAAAUAMAQAAAEAIAqPAgAAgwQAMJACAAAbABCRAgAAgwQAMJICAgAAAAGYAkAAyQMAIacCAQDIAwAhzAJAAMkDACH9AgEAyAMAIf8CAQDIAwAhiwMBAAAAAQEAAAAYACABAAAAGAAgCo8CAACDBAAwkAIAABsAEJECAACDBAAwkgICAMcDACGYAkAAyQMAIacCAQDIAwAhzAJAAMkDACH9AgEAyAMAIf8CAQDIAwAhiwMBAMgDACEBmQMAALwFACADAAAAGwAgAwAAHAAwBAAAGAAgAwAAABsAIAMAABwAMAQAABgAIAMAAAAbACADAAAcADAEAAAYACAHkgICAAAAAZgCQAAAAAGnAgEAAAABzAJAAAAAAf0CAQAAAAH_AgEAAAABiwMBAAAAAQEIAAAgACAHkgICAAAAAZgCQAAAAAGnAgEAAAABzAJAAAAAAf0CAQAAAAH_AgEAAAABiwMBAAAAAQEIAAAiADAHkgICAI4EACGYAkAAjAQAIacCAQCWBAAhzAJAAIwEACH9AgEAlgQAIf8CAQCWBAAhiwMBAJYEACECAAAAGAAgCAAAJAAgB5ICAgCOBAAhmAJAAIwEACGnAgEAlgQAIcwCQACMBAAh_QIBAJYEACH_AgEAlgQAIYsDAQCWBAAhAgAAABsAIAgAACYAIAMAAAAYACANAAAgACAOAAAkACABAAAAGAAgAQAAABsAIAUTAAC3BQAgFAAAuAUAIBUAALsFACAWAAC6BQAgFwAAuQUAIAqPAgAAggQAMJACAAAsABCRAgAAggQAMJICAgCoAwAhmAJAAKkDACGnAgEAtgMAIcwCQACpAwAh_QIBALYDACH_AgEAtgMAIYsDAQC2AwAhAwAAABsAIAMAACsAMBIAACwAIAMAAAAbACADAAAcADAEAAAYACAJCAAAgQQAII8CAACABAAwkAIAADIAEJECAACABAAwkgICAAAAAZgCQADJAwAhtwIBAMgDACHMAkAAyQMAIYoDAQDIAwAhAQAAAC8AIAEAAAAvACAJCAAAgQQAII8CAACABAAwkAIAADIAEJECAACABAAwkgICAMcDACGYAkAAyQMAIbcCAQDIAwAhzAJAAMkDACGKAwEAyAMAIQGZAwAAtgUAIAMAAAAyACADAAAzADAEAAAvACADAAAAMgAgAwAAMwAwBAAALwAgAwAAADIAIAMAADMAMAQAAC8AIAYIgAAAAAGSAgIAAAABmAJAAAAAAbcCAQAAAAHMAkAAAAABigMBAAAAAQEIAAA3ACAGCIAAAAABkgICAAAAAZgCQAAAAAG3AgEAAAABzAJAAAAAAYoDAQAAAAEBCAAAOQAwBgiAAAAAAZICAgCOBAAhmAJAAIwEACG3AgEAlgQAIcwCQACMBAAhigMBAJYEACECAAAALwAgCAAAOwAgBgiAAAAAAZICAgCOBAAhmAJAAIwEACG3AgEAlgQAIcwCQACMBAAhigMBAJYEACECAAAAMgAgCAAAPQAgAwAAAC8AIA0AADcAIA4AADsAIAEAAAAvACABAAAAMgAgBRMAALEFACAUAACyBQAgFQAAtQUAIBYAALQFACAXAACzBQAgCQgAAP4DACCPAgAA_QMAMJACAABDABCRAgAA_QMAMJICAgCoAwAhmAJAAKkDACG3AgEAtgMAIcwCQACpAwAhigMBALYDACEDAAAAMgAgAwAAQgAwEgAAQwAgAwAAADIAIAMAADMAMAQAAC8AIA43AAD5AwAgjwIAAPgDADCQAgAATgAQkQIAAPgDADCSAgIAAAABmAJAAMkDACG3AgEAAAABzAJAAMkDACHQAgEAyAMAIf0CAQDIAwAhhQMBAMgDACGGAwEAyAMAIYcDAQDIAwAhiAMgANYDACEBAAAARgAgCjYAAPwDACCPAgAA-wMAMJACAABIABCRAgAA-wMAMJICAgDHAwAhmAJAAMkDACHMAkAAyQMAId0CAQDIAwAhgwMCAMcDACGEAwgA4AMAIQI2AACvBQAgmQMAALAFACALNgAA_AMAII8CAAD7AwAwkAIAAEgAEJECAAD7AwAwkgICAAAAAZgCQADJAwAhzAJAAMkDACHdAgEAyAMAIYMDAgDHAwAhhAMIAOADACGJAwAA-gMAIAMAAABIACADAABJADAEAABKACABAAAASAAgAQAAAEYAIA43AAD5AwAgjwIAAPgDADCQAgAATgAQkQIAAPgDADCSAgIAxwMAIZgCQADJAwAhtwIBAMgDACHMAkAAyQMAIdACAQDIAwAh_QIBAMgDACGFAwEAyAMAIYYDAQDIAwAhhwMBAMgDACGIAyAA1gMAIQI3AACtBQAgmQMAAK4FACADAAAATgAgAwAATwAwBAAARgAgAwAAAE4AIAMAAE8AMAQAAEYAIAMAAABOACADAABPADAEAABGACALNwAArAUAIJICAgAAAAGYAkAAAAABtwIBAAAAAcwCQAAAAAHQAgEAAAAB_QIBAAAAAYUDAQAAAAGGAwEAAAABhwMBAAAAAYgDIAAAAAEBCAAAUwAgCpICAgAAAAGYAkAAAAABtwIBAAAAAcwCQAAAAAHQAgEAAAAB_QIBAAAAAYUDAQAAAAGGAwEAAAABhwMBAAAAAYgDIAAAAAEBCAAAVQAwCzcAAJ8FACCSAgIAjgQAIZgCQACMBAAhtwIBAJYEACHMAkAAjAQAIdACAQCWBAAh_QIBAJYEACGFAwEAlgQAIYYDAQCWBAAhhwMBAJYEACGIAyAAoQQAIQIAAABGACAIAABXACAKkgICAI4EACGYAkAAjAQAIbcCAQCWBAAhzAJAAIwEACHQAgEAlgQAIf0CAQCWBAAhhQMBAJYEACGGAwEAlgQAIYcDAQCWBAAhiAMgAKEEACECAAAATgAgCAAAWQAgAwAAAEYAIA0AAFMAIA4AAFcAIAEAAABGACABAAAATgAgBRMAAJoFACAUAACbBQAgFQAAngUAIBYAAJ0FACAXAACcBQAgDY8CAAD3AwAwkAIAAF8AEJECAAD3AwAwkgICAKgDACGYAkAAqQMAIbcCAQC2AwAhzAJAAKkDACHQAgEAtgMAIf0CAQC2AwAhhQMBALYDACGGAwEAtgMAIYcDAQC2AwAhiAMgAL8DACEDAAAATgAgAwAAXgAwEgAAXwAgAwAAAE4AIAMAAE8AMAQAAEYAIAEAAABKACABAAAASgAgAwAAAEgAIAMAAEkAMAQAAEoAIAMAAABIACADAABJADAEAABKACADAAAASAAgAwAASQAwBAAASgAgBzYAAJkFACCSAgIAAAABmAJAAAAAAcwCQAAAAAHdAgEAAAABgwMCAAAAAYQDCAAAAAEBCAAAZwAgBpICAgAAAAGYAkAAAAABzAJAAAAAAd0CAQAAAAGDAwIAAAABhAMIAAAAAQEIAABpADAHNgAAmAUAIJICAgCOBAAhmAJAAIwEACHMAkAAjAQAId0CAQCWBAAhgwMCAI4EACGEAwgA5gQAIQIAAABKACAIAABrACAGkgICAI4EACGYAkAAjAQAIcwCQACMBAAh3QIBAJYEACGDAwIAjgQAIYQDCADmBAAhAgAAAEgAIAgAAG0AIAMAAABKACANAABnACAOAABrACABAAAASgAgAQAAAEgAIAUTAACTBQAgFAAAlAUAIBUAAJcFACAWAACWBQAgFwAAlQUAIAmPAgAA9gMAMJACAABzABCRAgAA9gMAMJICAgCoAwAhmAJAAKkDACHMAkAAqQMAId0CAQC2AwAhgwMCAKgDACGEAwgA3QMAIQMAAABIACADAAByADASAABzACADAAAASAAgAwAASQAwBAAASgAgDY8CAAD0AwAwkAIAAHkAEJECAAD0AwAwkgICAAAAAZgCQADJAwAh0AIBAMgDACHYAgEAyAMAIf0CAQDIAwAh_gIBAMgDACH_AgEA1AMAIYADQADJAwAhgQMCAMcDACGCAwAA9QMAIAEAAAB2ACABAAAAdgAgDI8CAAD0AwAwkAIAAHkAEJECAAD0AwAwkgICAMcDACGYAkAAyQMAIdACAQDIAwAh2AIBAMgDACH9AgEAyAMAIf4CAQDIAwAh_wIBANQDACGAA0AAyQMAIYEDAgDHAwAhAv8CAACGBAAgmQMAAJIFACADAAAAeQAgAwAAegAwBAAAdgAgAwAAAHkAIAMAAHoAMAQAAHYAIAMAAAB5ACADAAB6ADAEAAB2ACAJkgICAAAAAZgCQAAAAAHQAgEAAAAB2AIBAAAAAf0CAQAAAAH-AgEAAAAB_wIBAAAAAYADQAAAAAGBAwIAAAABAQgAAH4AIAmSAgIAAAABmAJAAAAAAdACAQAAAAHYAgEAAAAB_QIBAAAAAf4CAQAAAAH_AgEAAAABgANAAAAAAYEDAgAAAAEBCAAAgAEAMAmSAgIAjgQAIZgCQACMBAAh0AIBAJYEACHYAgEAlgQAIf0CAQCWBAAh_gIBAJYEACH_AgEAnwQAIYADQACMBAAhgQMCAI4EACECAAAAdgAgCAAAggEAIAmSAgIAjgQAIZgCQACMBAAh0AIBAJYEACHYAgEAlgQAIf0CAQCWBAAh_gIBAJYEACH_AgEAnwQAIYADQACMBAAhgQMCAI4EACECAAAAeQAgCAAAhAEAIAMAAAB2ACANAAB-ACAOAACCAQAgAQAAAHYAIAEAAAB5ACAGEwAAjQUAIBQAAI4FACAVAACRBQAgFgAAkAUAIBcAAI8FACD_AgAAhgQAIAyPAgAA8wMAMJACAACKAQAQkQIAAPMDADCSAgIAqAMAIZgCQACpAwAh0AIBALYDACHYAgEAtgMAIf0CAQC2AwAh_gIBALYDACH_AgEAvQMAIYADQACpAwAhgQMCAKgDACEDAAAAeQAgAwAAiQEAMBIAAIoBACADAAAAeQAgAwAAegAwBAAAdgAgDY8CAADyAwAwkAIAAJABABCRAgAA8gMAMJICAgAAAAGYAkAAyQMAId0CAQDIAwAh9gIBAMgDACH3AggA4AMAIfgCCADgAwAh-QIBAMgDACH6AgEAyAMAIfsCCADgAwAh_AIIAOADACEBAAAAjQEAIAEAAACNAQAgDY8CAADyAwAwkAIAAJABABCRAgAA8gMAMJICAgDHAwAhmAJAAMkDACHdAgEAyAMAIfYCAQDIAwAh9wIIAOADACH4AggA4AMAIfkCAQDIAwAh-gIBAMgDACH7AggA4AMAIfwCCADgAwAhAZkDAACMBQAgAwAAAJABACADAACRAQAwBAAAjQEAIAMAAACQAQAgAwAAkQEAMAQAAI0BACADAAAAkAEAIAMAAJEBADAEAACNAQAgCpICAgAAAAGYAkAAAAAB3QIBAAAAAfYCAQAAAAH3AggAAAAB-AIIAAAAAfkCAQAAAAH6AgEAAAAB-wIIAAAAAfwCCAAAAAEBCAAAlQEAIAqSAgIAAAABmAJAAAAAAd0CAQAAAAH2AgEAAAAB9wIIAAAAAfgCCAAAAAH5AgEAAAAB-gIBAAAAAfsCCAAAAAH8AggAAAABAQgAAJcBADAKkgICAI4EACGYAkAAjAQAId0CAQCWBAAh9gIBAJYEACH3AggA5gQAIfgCCADmBAAh-QIBAJYEACH6AgEAlgQAIfsCCADmBAAh_AIIAOYEACECAAAAjQEAIAgAAJkBACAKkgICAI4EACGYAkAAjAQAId0CAQCWBAAh9gIBAJYEACH3AggA5gQAIfgCCADmBAAh-QIBAJYEACH6AgEAlgQAIfsCCADmBAAh_AIIAOYEACECAAAAkAEAIAgAAJsBACADAAAAjQEAIA0AAJUBACAOAACZAQAgAQAAAI0BACABAAAAkAEAIAUTAACHBQAgFAAAiAUAIBUAAIsFACAWAACKBQAgFwAAiQUAIA2PAgAA8QMAMJACAAChAQAQkQIAAPEDADCSAgIAqAMAIZgCQACpAwAh3QIBALYDACH2AgEAtgMAIfcCCADdAwAh-AIIAN0DACH5AgEAtgMAIfoCAQC2AwAh-wIIAN0DACH8AggA3QMAIQMAAACQAQAgAwAAoAEAMBIAAKEBACADAAAAkAEAIAMAAJEBADAEAACNAQAgHI8CAADwAwAwkAIAAKcBABCRAgAA8AMAMJICAgAAAAGYAkAAyQMAIbcCAQDIAwAhygIBAMgDACHMAkAAyQMAIdwCAQAAAAHiAgQA7QMAIeMCBADtAwAh5AICAMcDACHlAgIAxwMAIecCAQDIAwAh6AIBAMgDACHpAgEAyAMAIeoCBADtAwAh6wIEAO0DACHsAgIAxwMAIe0CCADgAwAh7gIEAO0DACHvAgQA7QMAIfACCADgAwAh8QIIAOADACHyAggA4AMAIfMCBADtAwAh9AIEAO0DACH1AkAAyQMAIQEAAACkAQAgAQAAAKQBACAcjwIAAPADADCQAgAApwEAEJECAADwAwAwkgICAMcDACGYAkAAyQMAIbcCAQDIAwAhygIBAMgDACHMAkAAyQMAIdwCAQDIAwAh4gIEAO0DACHjAgQA7QMAIeQCAgDHAwAh5QICAMcDACHnAgEAyAMAIegCAQDIAwAh6QIBAMgDACHqAgQA7QMAIesCBADtAwAh7AICAMcDACHtAggA4AMAIe4CBADtAwAh7wIEAO0DACHwAggA4AMAIfECCADgAwAh8gIIAOADACHzAgQA7QMAIfQCBADtAwAh9QJAAMkDACEBmQMAAIYFACADAAAApwEAIAMAAKgBADAEAACkAQAgAwAAAKcBACADAACoAQAwBAAApAEAIAMAAACnAQAgAwAAqAEAMAQAAKQBACAZkgICAAAAAZgCQAAAAAG3AgEAAAABygIBAAAAAcwCQAAAAAHcAgEAAAAB4gIEAAAAAeMCBAAAAAHkAgIAAAAB5QICAAAAAecCAQAAAAHoAgEAAAAB6QIBAAAAAeoCBAAAAAHrAgQAAAAB7AICAAAAAe0CCAAAAAHuAgQAAAAB7wIEAAAAAfACCAAAAAHxAggAAAAB8gIIAAAAAfMCBAAAAAH0AgQAAAAB9QJAAAAAAQEIAACsAQAgGZICAgAAAAGYAkAAAAABtwIBAAAAAcoCAQAAAAHMAkAAAAAB3AIBAAAAAeICBAAAAAHjAgQAAAAB5AICAAAAAeUCAgAAAAHnAgEAAAAB6AIBAAAAAekCAQAAAAHqAgQAAAAB6wIEAAAAAewCAgAAAAHtAggAAAAB7gIEAAAAAe8CBAAAAAHwAggAAAAB8QIIAAAAAfICCAAAAAHzAgQAAAAB9AIEAAAAAfUCQAAAAAEBCAAArgEAMBmSAgIAjgQAIZgCQACMBAAhtwIBAJYEACHKAgEAlgQAIcwCQACMBAAh3AIBAJYEACHiAgQA_wQAIeMCBAD_BAAh5AICAI4EACHlAgIAjgQAIecCAQCWBAAh6AIBAJYEACHpAgEAlgQAIeoCBAD_BAAh6wIEAP8EACHsAgIAjgQAIe0CCADmBAAh7gIEAP8EACHvAgQA_wQAIfACCADmBAAh8QIIAOYEACHyAggA5gQAIfMCBAD_BAAh9AIEAP8EACH1AkAAjAQAIQIAAACkAQAgCAAAsAEAIBmSAgIAjgQAIZgCQACMBAAhtwIBAJYEACHKAgEAlgQAIcwCQACMBAAh3AIBAJYEACHiAgQA_wQAIeMCBAD_BAAh5AICAI4EACHlAgIAjgQAIecCAQCWBAAh6AIBAJYEACHpAgEAlgQAIeoCBAD_BAAh6wIEAP8EACHsAgIAjgQAIe0CCADmBAAh7gIEAP8EACHvAgQA_wQAIfACCADmBAAh8QIIAOYEACHyAggA5gQAIfMCBAD_BAAh9AIEAP8EACH1AkAAjAQAIQIAAACnAQAgCAAAsgEAIAMAAACkAQAgDQAArAEAIA4AALABACABAAAApAEAIAEAAACnAQAgBRMAAIEFACAUAACCBQAgFQAAhQUAIBYAAIQFACAXAACDBQAgHI8CAADvAwAwkAIAALgBABCRAgAA7wMAMJICAgCoAwAhmAJAAKkDACG3AgEAtgMAIcoCAQC2AwAhzAJAAKkDACHcAgEAtgMAIeICBADpAwAh4wIEAOkDACHkAgIAqAMAIeUCAgCoAwAh5wIBALYDACHoAgEAtgMAIekCAQC2AwAh6gIEAOkDACHrAgQA6QMAIewCAgCoAwAh7QIIAN0DACHuAgQA6QMAIe8CBADpAwAh8AIIAN0DACHxAggA3QMAIfICCADdAwAh8wIEAOkDACH0AgQA6QMAIfUCQACpAwAhAwAAAKcBACADAAC3AQAwEgAAuAEAIAMAAACnAQAgAwAAqAEAMAQAAKQBACAQjwIAAOwDADCQAgAAvgEAEJECAADsAwAwkgICAAAAAZgCQADJAwAh3AIBAMgDACHdAkAAyQMAId4CCADgAwAh3wIEAO0DACHgAggA4AMAIeECBADtAwAh4gIEAO0DACHjAgQA7QMAIeQCAgDHAwAh5QICAMcDACHmAgAA7gMAIAEAAAC7AQAgAQAAALsBACAPjwIAAOwDADCQAgAAvgEAEJECAADsAwAwkgICAMcDACGYAkAAyQMAIdwCAQDIAwAh3QJAAMkDACHeAggA4AMAId8CBADtAwAh4AIIAOADACHhAgQA7QMAIeICBADtAwAh4wIEAO0DACHkAgIAxwMAIeUCAgDHAwAhAZkDAACABQAgAwAAAL4BACADAAC_AQAwBAAAuwEAIAMAAAC-AQAgAwAAvwEAMAQAALsBACADAAAAvgEAIAMAAL8BADAEAAC7AQAgDJICAgAAAAGYAkAAAAAB3AIBAAAAAd0CQAAAAAHeAggAAAAB3wIEAAAAAeACCAAAAAHhAgQAAAAB4gIEAAAAAeMCBAAAAAHkAgIAAAAB5QICAAAAAQEIAADDAQAgDJICAgAAAAGYAkAAAAAB3AIBAAAAAd0CQAAAAAHeAggAAAAB3wIEAAAAAeACCAAAAAHhAgQAAAAB4gIEAAAAAeMCBAAAAAHkAgIAAAAB5QICAAAAAQEIAADFAQAwDJICAgCOBAAhmAJAAIwEACHcAgEAlgQAId0CQACMBAAh3gIIAOYEACHfAgQA_wQAIeACCADmBAAh4QIEAP8EACHiAgQA_wQAIeMCBAD_BAAh5AICAI4EACHlAgIAjgQAIQIAAAC7AQAgCAAAxwEAIAySAgIAjgQAIZgCQACMBAAh3AIBAJYEACHdAkAAjAQAId4CCADmBAAh3wIEAP8EACHgAggA5gQAIeECBAD_BAAh4gIEAP8EACHjAgQA_wQAIeQCAgCOBAAh5QICAI4EACECAAAAvgEAIAgAAMkBACADAAAAuwEAIA0AAMMBACAOAADHAQAgAQAAALsBACABAAAAvgEAIAUTAAD6BAAgFAAA-wQAIBUAAP4EACAWAAD9BAAgFwAA_AQAIA-PAgAA6AMAMJACAADPAQAQkQIAAOgDADCSAgIAqAMAIZgCQACpAwAh3AIBALYDACHdAkAAqQMAId4CCADdAwAh3wIEAOkDACHgAggA3QMAIeECBADpAwAh4gIEAOkDACHjAgQA6QMAIeQCAgCoAwAh5QICAKgDACEDAAAAvgEAIAMAAM4BADASAADPAQAgAwAAAL4BACADAAC_AQAwBAAAuwEAIAyPAgAA5wMAMJACAADVAQAQkQIAAOcDADCSAgIAAAABmAJAAMkDACG3AgEAAAABygIBAMgDACHMAkAAyQMAIdgCAQDIAwAh2QICAMcDACHaAgEAyAMAIdsCQADJAwAhAQAAANIBACABAAAA0gEAIAyPAgAA5wMAMJACAADVAQAQkQIAAOcDADCSAgIAxwMAIZgCQADJAwAhtwIBAMgDACHKAgEAyAMAIcwCQADJAwAh2AIBAMgDACHZAgIAxwMAIdoCAQDIAwAh2wJAAMkDACEBmQMAAPkEACADAAAA1QEAIAMAANYBADAEAADSAQAgAwAAANUBACADAADWAQAwBAAA0gEAIAMAAADVAQAgAwAA1gEAMAQAANIBACAJkgICAAAAAZgCQAAAAAG3AgEAAAABygIBAAAAAcwCQAAAAAHYAgEAAAAB2QICAAAAAdoCAQAAAAHbAkAAAAABAQgAANoBACAJkgICAAAAAZgCQAAAAAG3AgEAAAABygIBAAAAAcwCQAAAAAHYAgEAAAAB2QICAAAAAdoCAQAAAAHbAkAAAAABAQgAANwBADAJkgICAI4EACGYAkAAjAQAIbcCAQCWBAAhygIBAJYEACHMAkAAjAQAIdgCAQCWBAAh2QICAI4EACHaAgEAlgQAIdsCQACMBAAhAgAAANIBACAIAADeAQAgCZICAgCOBAAhmAJAAIwEACG3AgEAlgQAIcoCAQCWBAAhzAJAAIwEACHYAgEAlgQAIdkCAgCOBAAh2gIBAJYEACHbAkAAjAQAIQIAAADVAQAgCAAA4AEAIAMAAADSAQAgDQAA2gEAIA4AAN4BACABAAAA0gEAIAEAAADVAQAgBRMAAPQEACAUAAD1BAAgFQAA-AQAIBYAAPcEACAXAAD2BAAgDI8CAADmAwAwkAIAAOYBABCRAgAA5gMAMJICAgCoAwAhmAJAAKkDACG3AgEAtgMAIcoCAQC2AwAhzAJAAKkDACHYAgEAtgMAIdkCAgCoAwAh2gIBALYDACHbAkAAqQMAIQMAAADVAQAgAwAA5QEAMBIAAOYBACADAAAA1QEAIAMAANYBADAEAADSAQAgCI8CAADlAwAwkAIAAOwBABCRAgAA5QMAMJICAgAAAAGYAkAAyQMAIcwCQADJAwAh1gIBAAAAAdcCAgDHAwAhAQAAAOkBACABAAAA6QEAIAiPAgAA5QMAMJACAADsAQAQkQIAAOUDADCSAgIAxwMAIZgCQADJAwAhzAJAAMkDACHWAgEAyAMAIdcCAgDHAwAhAZkDAADzBAAgAwAAAOwBACADAADtAQAwBAAA6QEAIAMAAADsAQAgAwAA7QEAMAQAAOkBACADAAAA7AEAIAMAAO0BADAEAADpAQAgBZICAgAAAAGYAkAAAAABzAJAAAAAAdYCAQAAAAHXAgIAAAABAQgAAPEBACAFkgICAAAAAZgCQAAAAAHMAkAAAAAB1gIBAAAAAdcCAgAAAAEBCAAA8wEAMAWSAgIAjgQAIZgCQACMBAAhzAJAAIwEACHWAgEAlgQAIdcCAgCOBAAhAgAAAOkBACAIAAD1AQAgBZICAgCOBAAhmAJAAIwEACHMAkAAjAQAIdYCAQCWBAAh1wICAI4EACECAAAA7AEAIAgAAPcBACADAAAA6QEAIA0AAPEBACAOAAD1AQAgAQAAAOkBACABAAAA7AEAIAUTAADuBAAgFAAA7wQAIBUAAPIEACAWAADxBAAgFwAA8AQAIAiPAgAA5AMAMJACAAD9AQAQkQIAAOQDADCSAgIAqAMAIZgCQACpAwAhzAJAAKkDACHWAgEAtgMAIdcCAgCoAwAhAwAAAOwBACADAAD8AQAwEgAA_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wIBAAAAAcwCQAAAAAEBCAAAuAIAMAaSAgIAjgQAIcgCAQCWBAAhyQIBAJYEACHKAgEAlgQAIcsCAQCWBAAhzAJAAIwEACECAAAArgIAIAgAALoCACAGkgICAI4EACHIAgEAlgQAIckCAQCWBAAhygIBAJYEACHLAgEAlgQAIcwCQACMBAAhAgAAALECACAIAAC8AgAgAwAAAK4CACANAAC2AgAgDgAAugIAIAEAAACuAgAgAQAAALECACAFEwAA2wQAIBQAANwEACAVAADfBAAgFgAA3gQAIBcAAN0EACAJjwIAANoDADCQAgAAwgIAEJECAADaAwAwkgICAKgDACHIAgEAtgMAIckCAQC2AwAhygIBALYDACHLAgEAtgMAIcwCQACpAwAhAwAAALECACADAADBAgAwEgAAwgIAIAMAAACxAgAgAwAAsgIAMAQAAK4CACAI1gEAAMoDACCPAgAAxgMAMJACAADXAgAQkQIAAMYDADCSAgIAAAABmAJAAMkDACG1AgEAAAABtwIBAMgDACEBAAAAxQIAIBfSAQAA1wMAINQBAADYAwAg1QEAANkDACCPAgAA0wMAMJACAADHAgAQkQIAANMDADCSAgIAxwMAIZgCQADJAwAhtAIBAMgDACG1AgEAyAMAIbYCAQDIAwAhtwIBAMgDACG4AgEA1AMAIbkCAgDHAwAhugICAMcDACG7AgIAxwMAIbwCAgDHAwAhvQICAMcDACG-AgIAxwMAIb8CAgDVAwAhwAJAANIDACHBAiAA1gMAIcICQADSAwAhCNIBAADXBAAg1AEAANgEACDVAQAA2QQAILgCAACGBAAgvwIAAIYEACDAAgAAhgQAIMICAACGBAAgmQMAANoEACAX0gEAANcDACDUAQAA2AMAINUBAADZAwAgjwIAANMDADCQAgAAxwIAEJECAADTAwAwkgICAAAAAZgCQADJAwAhtAIBAAAAAbUCAQDIAwAhtgIBAMgDACG3AgEAyAMAIbgCAQDUAwAhuQICAMcDACG6AgIAxwMAIbsCAgDHAwAhvAICAMcDACG9AgIAxwMAIb4CAgDHAwAhvwICANUDACHAAkAA0gMAIcECIADWAwAhwgJAANIDACEDAAAAxwIAIAMAAMgCADAEAADJAgAgE9MBAADPAwAgjwIAANEDADCQAgAAywIAEJECAADRAwAwkgICAMcDACGTAgIAxwMAIZUCCADNAwAhlgIIAM0DACGXAgAAzgMAIJgCQADJAwAhpwJAAMkDACGoAggAzQMAIakCCADNAwAhqgIIAM0DACGrAggAzQMAIawCCADNAwAhrQIIAM0DACGuAgEAyAMAIa8CQADSAwAhDNMBAADVBAAglQIAAIYEACCWAgAAhgQAIJcCAACGBAAgqAIAAIYEACCpAgAAhgQAIKoCAACGBAAgqwIAAIYEACCsAgAAhgQAIK0CAACGBAAgrwIAAIYEACCZAwAA1gQAIBTTAQAAzwMAII8CAADRAwAwkAIAAMsCABCRAgAA0QMAMJICAgAAAAGTAgIAxwMAIZUCCADNAwAhlgIIAM0DACGXAgAAzgMAIJgCQADJAwAhpwJAAMkDACGoAggAzQMAIakCCADNAwAhqgIIAM0DACGrAggAzQMAIawCCADNAwAhrQIIAM0DACGuAgEAyAMAIa8CQADSAwAhxwIAANADACADAAAAywIAIAMAAMwCADAEAADNAgAgC9MBAADPAwAgjwIAAMwDADCQAgAAzwIAEJECAADMAwAwkgICAMcDACGTAgIAxwMAIZQCQADJAwAhlQIIAM0DACGWAggAzQMAIZcCAADOAwAgmAJAAMkDACEE0wEAANUEACCVAgAAhgQAIJYCAACGBAAglwIAAIYEACAM0wEAAM8DACCPAgAAzAMAMJACAADPAgAQkQIAAMwDADCSAgIAAAABkwICAMcDACGUAkAAyQMAIZUCCADNAwAhlgIIAM0DACGXAgAAzgMAIJgCQADJAwAhxgIAAMsDACADAAAAzwIAIAMAANACADAEAADRAgAgAQAAAMsCACABAAAAzwIAIAEAAADHAgAgAQAAAMUCACAI1gEAAMoDACCPAgAAxgMAMJACAADXAgAQkQIAAMYDADCSAgIAxwMAIZgCQADJAwAhtQIBAMgDACG3AgEAyAMAIQLWAQAA0wQAIJkDAADUBAAgAwAAANcCACADAADYAgAwBAAAxQIAIAMAAADXAgAgAwAA2AIAMAQAAMUCACADAAAA1wIAIAMAANgCADAEAADFAgAgBdYBAADSBAAgkgICAAAAAZgCQAAAAAG1AgEAAAABtwIBAAAAAQEIAADcAgAgBJICAgAAAAGYAkAAAAABtQIBAAAAAbcCAQAAAAEBCAAA3gIAMAXWAQAAxQQAIJICAgCOBAAhmAJAAIwEACG1AgEAlgQAIbcCAQCWBAAhAgAAAMUCACAIAADgAgAgBJICAgCOBAAhmAJAAIwEACG1AgEAlgQAIbcCAQCWBAAhAgAAANcCACAIAADiAgAgAwAAAMUCACANAADcAgAgDgAA4AIAIAEAAADFAgAgAQAAANcCACAFEwAAwAQAIBQAAMEEACAVAADEBAAgFgAAwwQAIBcAAMIEACAHjwIAAMUDADCQAgAA6AIAEJECAADFAwAwkgICAKgDACGYAkAAqQMAIbUCAQC2AwAhtwIBALYDACEDAAAA1wIAIAMAAOcCADASAADoAgAgAwAAANcCACADAADYAgAwBAAAxQIAIAEAAADJAgAgAQAAAMkCACADAAAAxwIAIAMAAMgCADAEAADJAgAgAwAAAMcCACADAADIAgAwBAAAyQIAIAMAAADHAgAgAwAAyAIAMAQAAMkCACAU0gEAAL0EACDUAQAAvgQAINUBAAC_BAAgkgICAAAAAZgCQAAAAAG0AgEAAAABtQIBAAAAAbYCAQAAAAG3AgEAAAABuAIBAAAAAbkCAgAAAAG6AgIAAAABuwICAAAAAbwCAgAAAAG9AgIAAAABvgICAAAAAb8CAgAAAAHAAkAAAAABwQIgAAAAAcICQAAAAAEBCAAA8AIAIBGSAgIAAAABmAJAAAAAAbQCAQAAAAG1AgEAAAABtgIBAAAAAbcCAQAAAAG4AgEAAAABuQICAAAAAboCAgAAAAG7AgIAAAABvAICAAAAAb0CAgAAAAG-AgIAAAABvwICAAAAAcACQAAAAAHBAiAAAAABwgJAAAAAAQEIAADyAgAwFNIBAACiBAAg1AEAAKMEACDVAQAApAQAIJICAgCOBAAhmAJAAIwEACG0AgEAlgQAIbUCAQCWBAAhtgIBAJYEACG3AgEAlgQAIbgCAQCfBAAhuQICAI4EACG6AgIAjgQAIbsCAgCOBAAhvAICAI4EACG9AgIAjgQAIb4CAgCOBAAhvwICAKAEACHAAkAAlwQAIcECIAChBAAhwgJAAJcEACECAAAAyQIAIAgAAPQCACARkgICAI4EACGYAkAAjAQAIbQCAQCWBAAhtQIBAJYEACG2AgEAlgQAIbcCAQCWBAAhuAIBAJ8EACG5AgIAjgQAIboCAgCOBAAhuwICAI4EACG8AgIAjgQAIb0CAgCOBAAhvgICAI4EACG_AgIAoAQAIcACQACXBAAhwQIgAKEEACHCAkAAlwQAIQIAAADHAgAgCAAA9gIAIAMAAADJAgAgDQAA8AIAIA4AAPQCACABAAAAyQIAIAEAAADHAgAgCRMAAJoEACAUAACbBAAgFQAAngQAIBYAAJ0EACAXAACcBAAguAIAAIYEACC_AgAAhgQAIMACAACGBAAgwgIAAIYEACAUjwIAALwDADCQAgAA_AIAEJECAAC8AwAwkgICAKgDACGYAkAAqQMAIbQCAQC2AwAhtQIBALYDACG2AgEAtgMAIbcCAQC2AwAhuAIBAL0DACG5AgIAqAMAIboCAgCoAwAhuwICAKgDACG8AgIAqAMAIb0CAgCoAwAhvgICAKgDACG_AgIAvgMAIcACQAC3AwAhwQIgAL8DACHCAkAAtwMAIQMAAADHAgAgAwAA-wIAMBIAAPwCACADAAAAxwIAIAMAAMgCADAEAADJAgAgAQAAAM0CACABAAAAzQIAIAMAAADLAgAgAwAAzAIAMAQAAM0CACADAAAAywIAIAMAAMwCADAEAADNAgAgAwAAAMsCACADAADMAgAwBAAAzQIAIBDTAQAAmQQAIJICAgAAAAGTAgIAAAABlQIIAAAAAZYCCAAAAAGXAoAAAAABmAJAAAAAAacCQAAAAAGoAggAAAABqQIIAAAAAaoCCAAAAAGrAggAAAABrAIIAAAAAa0CCAAAAAGuAgEAAAABrwJAAAAAAQEIAACEAwAgD5ICAgAAAAGTAgIAAAABlQIIAAAAAZYCCAAAAAGXAoAAAAABmAJAAAAAAacCQAAAAAGoAggAAAABqQIIAAAAAaoCCAAAAAGrAggAAAABrAIIAAAAAa0CCAAAAAGuAgEAAAABrwJAAAAAAQEIAACGAwAwENMBAACYBAAgkgICAI4EACGTAgIAjgQAIZUCCACNBAAhlgIIAI0EACGXAoAAAAABmAJAAIwEACGnAkAAjAQAIagCCACNBAAhqQIIAI0EACGqAggAjQQAIasCCACNBAAhrAIIAI0EACGtAggAjQQAIa4CAQCWBAAhrwJAAJcEACECAAAAzQIAIAgAAIgDACAPkgICAI4EACGTAgIAjgQAIZUCCACNBAAhlgIIAI0EACGXAoAAAAABmAJAAIwEACGnAkAAjAQAIagCCACNBAAhqQIIAI0EACGqAggAjQQAIasCCACNBAAhrAIIAI0EACGtAggAjQQAIa4CAQCWBAAhrwJAAJcEACECAAAAywIAIAgAAIoDACADAAAAzQIAIA0AAIQDACAOAACIAwAgAQAAAM0CACABAAAAywIAIA8TAACRBAAgFAAAkgQAIBUAAJUEACAWAACUBAAgFwAAkwQAIJUCAACGBAAglgIAAIYEACCXAgAAhgQAIKgCAACGBAAgqQIAAIYEACCqAgAAhgQAIKsCAACGBAAgrAIAAIYEACCtAgAAhgQAIK8CAACGBAAgEo8CAAC1AwAwkAIAAJADABCRAgAAtQMAMJICAgCoAwAhkwICAKgDACGVAggAqgMAIZYCCACqAwAhlwIAAKsDACCYAkAAqQMAIacCQACpAwAhqAIIAKoDACGpAggAqgMAIaoCCACqAwAhqwIIAKoDACGsAggAqgMAIa0CCACqAwAhrgIBALYDACGvAkAAtwMAIQMAAADLAgAgAwAAjwMAMBIAAJADACADAAAAywIAIAMAAMwCADAEAADNAgAgAQAAANECACABAAAA0QIAIAMAAADPAgAgAwAA0AIAMAQAANECACADAAAAzwIAIAMAANACADAEAADRAgAgAwAAAM8CACADAADQAgAwBAAA0QIAIAjTAQAAkAQAIJICAgAAAAGTAgIAAAABlAJAAAAAAZUCCAAAAAGWAggAAAABlwKAAAAAAZgCQAAAAAEBCAAAmAMAIAeSAgIAAAABkwICAAAAAZQCQAAAAAGVAggAAAABlgIIAAAAAZcCgAAAAAGYAkAAAAABAQgAAJoDADAI0wEAAI8EACCSAgIAjgQAIZMCAgCOBAAhlAJAAIwEACGVAggAjQQAIZYCCACNBAAhlwKAAAAAAZgCQACMBAAhAgAAANECACAIAACcAwAgB5ICAgCOBAAhkwICAI4EACGUAkAAjAQAIZUCCACNBAAhlgIIAI0EACGXAoAAAAABmAJAAIwEACECAAAAzwIAIAgAAJ4DACADAAAA0QIAIA0AAJgDACAOAACcAwAgAQAAANECACABAAAAzwIAIAgTAACHBAAgFAAAiAQAIBUAAIsEACAWAACKBAAgFwAAiQQAIJUCAACGBAAglgIAAIYEACCXAgAAhgQAIAqPAgAApwMAMJACAACkAwAQkQIAAKcDADCSAgIAqAMAIZMCAgCoAwAhlAJAAKkDACGVAggAqgMAIZYCCACqAwAhlwIAAKsDACCYAkAAqQMAIQMAAADPAgAgAwAAowMAMBIAAKQDACADAAAAzwIAIAMAANACADAEAADRAgAgCo8CAACnAwAwkAIAAKQDABCRAgAApwMAMJICAgCoAwAhkwICAKgDACGUAkAAqQMAIZUCCACqAwAhlgIIAKoDACGXAgAAqwMAIJgCQACpAwAhDRMAALEDACAUAAC0AwAgFQAAsQMAIBYAALEDACAXAACxAwAgmQICAAAAAaACAgAAAAGhAgIAAAABogICAAAAAaMCAgAAAAGkAgIAswMAIaUCAgAAAASmAgIAAAAECxMAALEDACAWAACyAwAgFwAAsgMAIJkCQAAAAAGgAkAAAAABoQJAAAAAAaICQAAAAAGjAkAAAAABpAJAALADACGlAkAAAAAEpgJAA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-AgIAqAMAIb8CAgC-AwAhwAJAALcDACHBAiAAvwMAIcICQAC3AwAhDxMAAKwDACAWAADEAwAgFwAAxAMAIJkCAQAAAAGgAgEAAAABoQIBAAAAAaICAQAAAAGjAgEAAAABpAIBAMMDACGlAgEAAAAFpgIBAAAABbACAQAAAAGxAgEAAAABsgIBAAAAAbMCAQAAAAENEwAArAMAIBQAAK8DACAVAACsAwAgFgAArAMAIBcAAKwDACCZAgIAAAABoAICAAAAAaECAgAAAAGiAgIAAAABowICAAAAAaQCAgDCAwAhpQICAAAABaYCAgAAAAUFEwAAsQMAIBYAAMEDACAXAADBAwAgmQIgAAAAAaQCIADAAwAhBRMAALEDACAWAADBAwAgFwAAwQMAIJkCIAAAAAGkAiAAwAMAIQKZAiAAAAABpAIgAMEDACENEwAArAMAIBQAAK8DACAVAACsAwAgFgAArAMAIBcAAKwDACCZAgIAAAABoAICAAAAAaECAgAAAAGiAgIAAAABowICAAAAAaQCAgDCAwAhpQICAAAABaYCAgAAAAUPEwAArAMAIBYAAMQDACAXAADEAwAgmQIBAAAAAaACAQAAAAGhAgEAAAABogIBAAAAAaMCAQAAAAGkAgEAwwMAIaUCAQAAAAWmAgEAAAAFsAIBAAAAAbECAQAAAAGyAgEAAAABswIBAAAAAQyZAgEAAAABoAIBAAAAAaECAQAAAAGiAgEAAAABowIBAAAAAaQCAQDEAwAhpQIBAAAABaYCAQAAAAWwAgEAAAABsQIBAAAAAbICAQAAAAGzAgEAAAABB48CAADFAwAwkAIAAOgCABCRAgAAxQMAMJICAgCoAwAhmAJAAKkDACG1AgEAtgMAIbcCAQC2AwAhCNYBAADKAwAgjwIAAMYDADCQAgAA1wIAEJECAADGAwAwkgICAMcDACGYAkAAyQMAIbUCAQDIAwAhtwIBAMgDACEImQICAAAAAaACAgAAAAGhAgIAAAABogICAAAAAaMCAgAAAAGkAgIAsQMAIaUCAgAAAASmAgIAAAAEDJkCAQAAAAGgAgEAAAABoQIBAAAAAaICAQAAAAGjAgEAAAABpAIBALsDACGlAgEAAAAEpgIBAAAABLACAQAAAAGxAgEAAAABsgIBAAAAAbMCAQAAAAEImQJAAAAAAaACQAAAAAGhAkAAAAABogJAAAAAAaMCQAAAAAGkAkAAsgMAIaUCQAAAAASmAkAAAAAEA8MCAADHAgAgxAIAAMcCACDFAgAAxwIAIAKTAgIAAAABlAJAAAAAAQvTAQAAzwMAII8CAADMAwAwkAIAAM8CABCRAgAAzAMAMJICAgDHAwAhkwICAMcDACGUAkAAyQMAIZUCCADNAwAhlgIIAM0DACGXAgAAzgMAIJgCQADJAwAhCJkCCAAAAAGgAggAAAABoQIIAAAAAaICCAAAAAGjAggAAAABpAIIAK8DACGlAggAAAAFpgIIAAAABQyZAoAAAAABmgIBAAAAAZsCAQAAAAGcAgEAAAABnQKAAAAAAZ4CgAAAAAGfAoAAAAABoAKAAAAAAaECgAAAAAGiAoAAAAABowKAAAAAAaQCgAAAAAEZ0gEAANcDACDUAQAA2AMAINUBAADZAwAgjwIAANMDADCQAgAAxwIAEJECAADTAwAwkgICAMcDACGYAkAAyQMAIbQCAQDIAwAhtQIBAMgDACG2AgEAyAMAIbcCAQDIAwAhuAIBANQDACG5AgIAxwMAIboCAgDHAwAhuwICAMcDACG8AgIAxwMAIb0CAgDHAwAhvgICAMcDACG_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-AIIAN0DACH5AgEAtgMAIfoCAQC2AwAh-wIIAN0DACH8AggA3QMAIQ2PAgAA8gMAMJACAACQAQAQkQIAAPIDADCSAgIAxwMAIZgCQADJAwAh3QIBAMgDACH2AgEAyAMAIfcCCADgAwAh-AIIAOADACH5AgEAyAMAIfoCAQDIAwAh-wIIAOADACH8AggA4AMAIQyPAgAA8wMAMJACAACKAQAQkQIAAPMDADCSAgIAqAMAIZgCQACpAwAh0AIBALYDACHYAgEAtgMAIf0CAQC2AwAh_gIBALYDACH_AgEAvQMAIYADQACpAwAhgQMCAKgDACEMjwIAAPQDADCQAgAAeQAQkQIAAPQDADCSAgIAxwMAIZgCQADJAwAh0AIBAMgDACHYAgEAyAMAIf0CAQDIAwAh_gIBAMgDACH_AgEA1AMAIYADQADJAwAhgQMCAMcDACEC_QIBAAAAAYADQAAAAAEJjwIAAPYDADCQAgAAcwAQkQIAAPYDADCSAgIAqAMAIZgCQACpAwAhzAJAAKkDACHdAgEAtgMAIYMDAgCoAwAhhAMIAN0DACENjwIAAPcDADCQAgAAXwAQkQIAAPcDADCSAgIAqAMAIZgCQACpAwAhtwIBALYDACHMAkAAqQMAIdACAQC2AwAh_QIBALYDACGFAwEAtgMAIYYDAQC2AwAhhwMBALYDACGIAyAAvwMAIQ43AAD5AwAgjwIAAPgDADCQAgAATgAQkQIAAPgDADCSAgIAxwMAIZgCQADJAwAhtwIBAMgDACHMAkAAyQMAIdACAQDIAwAh_QIBAMgDACGFAwEAyAMAIYYDAQDIAwAhhwMBAMgDACGIAyAA1gMAIQPDAgAASAAgxAIAAEgAIMUCAABIACAC3QIBAAAAAYMDAgAAAAEKNgAA_AMAII8CAAD7AwAwkAIAAEgAEJECAAD7AwAwkgICAMcDACGYAkAAyQMAIcwCQADJAwAh3QIBAMgDACGDAwIAxwMAIYQDCADgAwAhEDcAAPkDACCPAgAA-AMAMJACAABOABCRAgAA-AMAMJICAgDHAwAhmAJAAMkDACG3AgEAyAMAIcwCQADJAwAh0AIBAMgDACH9AgEAyAMAIYUDAQDIAwAhhgMBAMgDACGHAwEAyAMAIYgDIADWAwAhjgMAAE4AII8DAABOACAJCAAA_gMAII8CAAD9AwAwkAIAAEMAEJECAAD9AwAwkgICAKgDACGYAkAAqQMAIbcCAQC2AwAhzAJAAKkDACGKAwEAtgMAIQ8TAACxAwAgFgAA_wMAIBcAAP8DACCZAoAAAAABmgIBAAAAAZsCAQAAAAGcAgEAAAABnQKAAAAAAZ4CgAAAAAGfAoAAAAABoAKAAAAAAaECgAAAAAGiAoAAAAABowKAAAAAAaQCgAAAAAEMmQKAAAAAAZoCAQAAAAGbAgEAAAABnAIBAAAAAZ0CgAAAAAGeAoAAAAABnwKAAAAAAaACgAAAAAGhAoAAAAABogKAAAAAAaMCgAAAAAGkAoAAAAABCQgAAIEEACCPAgAAgAQAMJACAAAyABCRAgAAgAQAMJICAgDHAwAhmAJAAMkDACG3AgEAyAMAIcwCQADJAwAhigMBAMgDACEMmQKAAAAAAZoCAQAAAAGbAgEAAAABnAIBAAAAAZ0CgAAAAAGeAoAAAAABnwKAAAAAAaACgAAAAAGhAoAAAAABogKAAAAAAaMCgAAAAAGkAoAAAAABCo8CAACCBAAwkAIAACwAEJECAACCBAAwkgICAKgDACGYAkAAqQMAIacCAQC2AwAhzAJAAKkDACH9AgEAtgMAIf8CAQC2AwAhiwMBALYDACEKjwIAAIMEADCQAgAAGwAQkQIAAIMEADCSAgIAxwMAIZgCQADJAwAhpwIBAMgDACHMAkAAyQMAIf0CAQDIAwAh_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_BAAgkgICAAAAAZgCQAAAAAG0AgEAAAABtgIBAAAAAbcCAQAAAAG4AgEAAAABuQICAAAAAboCAgAAAAG7AgIAAAABvAICAAAAAb0CAgAAAAG-AgIAAAABvwICAAAAAcACQAAAAAHBAiAAAAABwgJAAAAAAQIAAADJAgAgDQAA0QQAIAMAAADJAgAgDQAA0QQAIA4AANAEACABCAAAyQUAMBfSAQAA1wMAINQBAADYAwAg1QEAANkDACCPAgAA0wMAMJACAADHAgAQkQIAANMDADCSAgIAAAABmAJAAMkDACG0AgEAAAABtQIBAMgDACG2AgEAyAMAIbcCAQDIAwAhuAIBANQDACG5AgIAxwMAIboCAgDHAwAhuwICAMcDACG8AgIAxwMAIb0CAgDHAwAhvgICAMcDACG_AgIA1QMAIcACQADSAwAhwQIgANYDACHCAkAA0gMAIQIAAADJAgAgCAAA0AQAIAIAAADOBAAgCAAAzwQAIBSPAgAAzQQAMJACAADOBAAQkQIAAM0EADCSAgIAxwMAIZgCQADJAwAhtAIBAMgDACG1AgEAyAMAIbYCAQDIAwAhtwIBAMgDACG4AgEA1AMAIbkCAgDHAwAhugICAMcDACG7AgIAxwMAIbwCAgDHAwAhvQICAMcDACG-AgIAxwMAIb8CAgDVAwAhwAJAANIDACHBAiAA1gMAIcICQADSAwAhFI8CAADNBAAwkAIAAM4EABCRAgAAzQQAMJICAgDHAwAhmAJAAMkDACG0AgEAyAMAIbUCAQDIAwAhtgIBAMgDACG3AgEAyAMAIbgCAQDUAwAhuQICAMcDACG6AgIAxwMAIbsCAgDHAwAhvAICAMcDACG9AgIAxwMAIb4CAgDHAwAhvwICANUDACHAAkAA0gMAIcECIADWAwAhwgJAANIDACEQkgICAI4EACGYAkAAjAQAIbQCAQCWBAAhtgIBAJYEACG3AgEAlgQAIbgCAQCfBAAhuQICAI4EACG6AgIAjgQAIbsCAgCOBAAhvAICAI4EACG9AgIAjgQAIb4CAgCOBAAhvwICAKAEACHAAkAAlwQAIcECIAChBAAhwgJAAJcEACES1AEAAKMEACDVAQAApAQAIJICAgCOBAAhmAJAAIwEACG0AgEAlgQAIbYCAQCWBAAhtwIBAJYEACG4AgEAnwQAIbkCAgCOBAAhugICAI4EACG7AgIAjgQAIbwCAgCOBAAhvQICAI4EACG-AgIAjgQAIb8CAgCgBAAhwAJAAJcEACHBAiAAoQQAIcICQACXBAAhEtQBAAC-BAAg1QEAAL8EACCSAgIAAAABmAJAAAAAAbQCAQAAAAG2AgEAAAABtwIBAAAAAbgCAQAAAAG5AgIAAAABugICAAAAAbsCAgAAAAG8AgIAAAABvQICAAAAAb4CAgAAAAG_AgIAAAABwAJAAAAAAcECIAAAAAHCAkAAAAABBA0AAMYEADCQAwAAxwQAMJIDAADJBAAglgMAAMoEADAAAbMCAQAAAAEI0gEAANcEACDUAQAA2AQAINUBAADZBAAguAIAAIYEACC_AgAAhgQAIMACAACGBAAgwgIAAIYEACCZAwAA2gQAIAGzAgEAAAABAtYBAADTBAAgmQMAANQEACAAAAGzAgEAAAABAAAAAAABswIBAAAAAQAAAAAABZMDCAAAAAGaAwgAAAABmwMIAAAAAZwDCAAAAAGdAwgAAAABAbMCAQAAAAEAAAAAAAGzAgEAAAABAAAAAAABswIBAAAAAQAAAAAAAbMCAQAAAAEAAAAAAAWTAwQAAAABmgMEAAAAAZsDBAAAAAGcAwQAAAABnQMEAAAAAQGzAgEAAAABAAAAAAABswIBAAAAAQAAAAAAAbMCAQAAAAEAAAAAAAGzAgEAAAABAAAAAAAFDQAAxAUAIA4AAMcFACCQAwAAxQUAIJEDAADGBQAglgMAAEYAIAMNAADEBQAgkAMAAMUFACCWAwAARgAgAAAAAAALDQAAoAUAMA4AAKUFADCQAwAAoQUAMJEDAACiBQAwkgMAAKMFACCTAwAApAUAMJQDAACkBQAwlQMAAKQFADCWAwAApAUAMJcDAACmBQAwmAMAAKcFADAFkgICAAAAAZgCQAAAAAHMAkAAAAAB3QIBAAAAAYQDCAAAAAECAAAASgAgDQAAqwUAIAMAAABKACANAACrBQAgDgAAqgUAIAEIAADDBQAwCzYAAPwDACCPAgAA-wMAMJACAABIABCRAgAA-wMAMJICAgAAAAGYAkAAyQMAIcwCQADJAwAh3QIBAMgDACGDAwIAxwMAIYQDCADgAwAhiQMAAPoDACACAAAASgAgCAAAqgUAIAIAAACoBQAgCAAAqQUAIAmPAgAApwUAMJACAACoBQAQkQIAAKcFADCSAgIAxwMAIZgCQADJAwAhzAJAAMkDACHdAgEAyAMAIYMDAgDHAwAhhAMIAOADACEJjwIAAKcFADCQAgAAqAUAEJECAACnBQAwkgICAMcDACGYAkAAyQMAIcwCQADJAwAh3QIBAMgDACGDAwIAxwMAIYQDCADgAwAhBZICAgCOBAAhmAJAAIwEACHMAkAAjAQAId0CAQCWBAAhhAMIAOYEACEFkgICAI4EACGYAkAAjAQAIcwCQACMBAAh3QIBAJYEACGEAwgA5gQAIQWSAgIAAAABmAJAAAAAAcwCQAAAAAHdAgEAAAABhAMIAAAAAQQNAACgBQAwkAMAAKEFADCSAwAAowUAIJYDAACkBQAwAAGzAgEAAAABAjcAAK0FACCZAwAArgUAIAGzAgEAAAABAAAAAAABswIBAAAAAQAAAAAAAbMCAQAAAAEAAAAAAAGzAgEAAAABBZICAgAAAAGYAkAAAAABzAJAAAAAAd0CAQAAAAGEAwgAAAABCpICAgAAAAGYAkAAAAABtwIBAAAAAcwCQAAAAAHQAgEAAAAB_QIBAAAAAYUDAQAAAAGGAwEAAAABhwMBAAAAAYgDIAAAAAECAAAARgAgDQAAxAUAIAMAAABOACANAADEBQAgDgAAyAUAIAwAAABOACAIAADIBQAgkgICAI4EACGYAkAAjAQAIbcCAQCWBAAhzAJAAIwEACHQAgEAlgQAIf0CAQCWBAAhhQMBAJYEACGGAwEAlgQAIYcDAQCWBAAhiAMgAKEEACEKkgICAI4EACGYAkAAjAQAIbcCAQCWBAAhzAJAAIwEACHQAgEAlgQAIf0CAQCWBAAhhQMBAJYEACGGAwEAlgQAIYcDAQCWBAAhiAMgAKEEACEQkgICAAAAAZgCQAAAAAG0AgEAAAABtgIBAAAAAbcCAQAAAAG4AgEAAAABuQICAAAAAboCAgAAAAG7AgIAAAABvAICAAAAAb0CAgAAAAG-AgIAAAABvwICAAAAAcACQAAAAAHBAiAAAAABwgJAAAAAAQSSAgIAAAABmAJAAAAAAbUCAQAAAAG3AgEAAAABAgAAAMUCACANAADKBQAgDpICAgAAAAGVAggAAAABlgIIAAAAAZcCgAAAAAGYAkAAAAABpwJAAAAAAagCCAAAAAGpAggAAAABqgIIAAAAAasCCAAAAAGsAggAAAABrQIIAAAAAa4CAQAAAAGvAkAAAAABBpICAgAAAAGUAkAAAAABlQIIAAAAAZYCCAAAAAGXAoAAAAABmAJAAAAAAQMAAADXAgAgDQAAygUAIA4AANAFACAGAAAA1wIAIAgAANAFACCSAgIAjgQAIZgCQACMBAAhtQIBAJYEACG3AgEAlgQAIQSSAgIAjgQAIZgCQACMBAAhtQIBAJYEACG3AgEAlgQAIRPSAQAAvQQAINUBAAC_BAAgkgICAAAAAZgCQAAAAAG0AgEAAAABtQIBAAAAAbYCAQAAAAG3AgEAAAABuAIBAAAAAbkCAgAAAAG6AgIAAAABuwICAAAAAbwCAgAAAAG9AgIAAAABvgICAAAAAb8CAgAAAAHAAkAAAAABwQIgAAAAAcICQAAAAAECAAAAyQIAIA0AANEFACADAAAAxwIAIA0AANEFACAOAADVBQAgFQAAAMcCACAIAADVBQAg0gEAAKIEACDVAQAApAQAIJICAgCOBAAhmAJAAIwEACG0AgEAlgQAIbUCAQCWBAAhtgIBAJYEACG3AgEAlgQAIbgCAQCfBAAhuQICAI4EACG6AgIAjgQAIbsCAgCOBAAhvAICAI4EACG9AgIAjgQAIb4CAgCOBAAhvwICAKAEACHAAkAAlwQAIcECIAChBAAhwgJAAJcEACET0gEAAKIEACDVAQAApAQAIJICAgCOBAAhmAJAAIwEACG0AgEAlgQAIbUCAQCWBAAhtgIBAJYEACG3AgEAlgQAIbgCAQCfBAAhuQICAI4EACG6AgIAjgQAIbsCAgCOBAAhvAICAI4EACG9AgIAjgQAIb4CAgCOBAAhvwICAKAEACHAAkAAlwQAIcECIAChBAAhwgJAAJcEACET0gEAAL0EACDUAQAAvgQAIJICAgAAAAGYAkAAAAABtAIBAAAAAbUCAQAAAAG2AgEAAAABtwIBAAAAAbgCAQAAAAG5AgIAAAABugICAAAAAbsCAgAAAAG8AgIAAAABvQICAAAAAb4CAgAAAAG_AgIAAAABwAJAAAAAAcECIAAAAAHCAkAAAAABAgAAAMkCACANAADWBQAgAwAAAMcCACANAADWBQAgDgAA2gUAIBUAAADHAgAgCAAA2gUAINIBAACiBAAg1AEAAKMEACCSAgIAjgQAIZgCQACMBAAhtAIBAJYEACG1AgEAlgQAIbYCAQCWBAAhtwIBAJYEACG4AgEAnwQAIbkCAgCOBAAhugICAI4EACG7AgIAjgQAIbwCAgCOBAAhvQICAI4EACG-AgIAjgQAIb8CAgCgBAAhwAJAAJcEACHBAiAAoQQAIcICQACXBAAhE9IBAACiBAAg1AEAAKMEACCSAgIAjgQAIZgCQACMBAAhtAIBAJYEACG1AgEAlgQAIbYCAQCWBAAhtwIBAJYEACG4AgEAnwQAIbkCAgCOBAAhugICAI4EACG7AgIAjgQAIbwCAgCOBAAhvQICAI4EACG-AgIAjgQAIb8CAgCgBAAhwAJAAJcEACHBAiAAoQQAIcICQACXBAAhAAAFEwAEFAAFFQAGFgAHFwAIAAAAAAAFEwAEFAAFFQAGFgAHFwAIAAUTAAwUAA0VAA4WAA8XABAAAAAAAAUTAAwUAA0VAA4WAA8XABAABRMAFBQAFRUAFhYAFxcAGAAAAAAABRMAFBQAFRUAFhYAFxcAGAITABw3SxsBNgAaATdMAAUTAB4UAB8VACAWACEXACIAAAAAAAUTAB4UAB8VACAWACEXACIFEwAlFAAmFQAnFgAoFwApAAAAAAAFEwAlFAAmFQAnFgAoFwApAAUTAC0UAC4VAC8WADAXADEAAAAAAAUTAC0UAC4VAC8WADAXADEABRMANRQANhUANxYAOBcAOQAAAAAABRMANRQANhUANxYAOBcAOQAFEwA9FAA-FQA_FgBAFwBBAAAAAAAFEwA9FAA-FQA_FgBAFwBBAAUTAEUUAEYVAEcWAEgXAEkAAAAAAAUTAEUUAEYVAEcWAEgXAEkABRMATRQAThUATxYAUBcAUQAAAAAABRMATRQAThUATxYAUBcAUQAFEwBVFABWFQBXFgBYFwBZAAAAAAAFEwBVFABWFQBXFgBYFwBZAAUTAF0UAF4VAF8WAGAXAGEAAAAAAAUTAF0UAF4VAF8WAGAXAGEABRMAZRQAZhUAZxYAaBcAaQAAAAAABRMAZRQAZhUAZxYAaBcAaQAFEwBtFABuFQBvFgBwFwBxAAAAAAAFEwBtFABuFQBvFgBwFwBxAhMAeNYBygJ0BBMAd9IBAHPUAc4CddUB0gJ2AdMBAHQB0wEAdALUAdMCANUB1AIAAdYB1QIABRMAehQAexUAfBYAfRcAfgAAAAAABRMAehQAexUAfBYAfRcAfgUTAIEBFACCARUAgwEWAIQBFwCFAQAAAAAABRMAgQEUAIIBFQCDARYAhAEXAIUBBRMAiAEUAIkBFQCKARYAiwEXAIwBAAAAAAAFEwCIARQAiQEVAIoBFgCLARcAjAEFEwCPARQAkAEVAJEBFgCSARcAkwEAAAAAAAUTAI8BFACQARUAkQEWAJIBFwCTAQECAQIDAQUGAQYHAQcIAQkKAQoMAgsOAQwQAg8RARASARETAhgWAxkXCRoZChsaChwdCh0eCh4fCh8hCiAjAiElCiInAiMoCiQpCiUqAiYtCycuESgwEikxEio0Eis1Eiw2Ei04Ei46Ai88EjA-AjE_EjJAEjNBAjREEzVFGThHGjlNGjpQGjtRGjxSGj1UGj5WAj9YGkBaAkFbGkJcGkNdAkRgHUVhI0ZiG0djG0hkG0llG0pmG0toG0xqAk1sG05uAk9vG1BwG1FxAlJ0JFN1KlR3K1V4K1Z7K1d8K1h9K1l_K1qBAQJbgwErXIUBAl2GAStehwErX4gBAmCLASxhjAEyYo4BM2OPATNkkgEzZZMBM2aUATNnlgEzaJgBAmmaATNqnAECa50BM2yeATNtnwECbqIBNG-jATpwpQE7caYBO3KpATtzqgE7dKsBO3WtATt2rwECd7EBO3izAQJ5tAE7erUBO3u2AQJ8uQE8fboBQn68AUN_vQFDgAHAAUOBAcEBQ4IBwgFDgwHEAUOEAcYBAoUByAFDhgHKAQKHAcsBQ4gBzAFDiQHNAQKKAdABRIsB0QFKjAHTAUuNAdQBS44B1wFLjwHYAUuQAdkBS5EB2wFLkgHdAQKTAd8BS5QB4QEClQHiAUuWAeMBS5cB5AECmAHnAUyZAegBUpoB6gFTmwHrAVOcAe4BU50B7wFTngHwAVOfAfIBU6AB9AECoQH2AVOiAfgBAqMB-QFTpAH6AVOlAfsBAqYB_gFUpwH_AVqoAYECW6kBggJbqgGFAlurAYYCW6wBhwJbrQGJAluuAYsCAq8BjQJbsAGPAgKxAZACW7IBkQJbswGSAgK0AZUCXLUBlgJitgGYAmO3AZkCY7gBnAJjuQGdAmO6AZ4CY7sBoAJjvAGiAgK9AaQCY74BpgICvwGnAmPAAagCY8EBqQICwgGsAmTDAa0CasQBrwJrxQGwAmvGAbMCa8cBtAJryAG1AmvJAbcCa8oBuQICywG7AmvMAb0CAs0BvgJrzgG_AmvPAcACAtABwwJs0QHEAnLXAcYCc9gB1gJz2QHZAnPaAdoCc9sB2wJz3AHdAnPdAd8CAt4B4QJz3wHjAgLgAeQCc-EB5QJz4gHmAgLjAekCeeQB6gJ_5QHrAnTmAewCdOcB7QJ06AHuAnTpAe8CdOoB8QJ06wHzAgLsAfUCdO0B9wIC7gH4AnTvAfkCdPAB-gIC8QH9AoAB8gH-AoYB8wH_AnX0AYADdfUBgQN19gGCA3X3AYMDdfgBhQN1-QGHAwL6AYkDdfsBiwMC_AGMA3X9AY0Ddf4BjgMC_wGRA4cBgAKSA40BgQKTA3aCApQDdoMClQN2hAKWA3aFApcDdoYCmQN2hwKbAwKIAp0DdokCnwMCigKgA3aLAqEDdowCogMCjQKlA44BjgKmA5QB" + strings: JSON.parse("[\"where\",\"orderBy\",\"cursor\",\"user\",\"habit\",\"records\",\"_count\",\"habits\",\"checkIns\",\"list\",\"items\",\"checkLists\",\"templates\",\"User.findUnique\",\"User.findUniqueOrThrow\",\"User.findFirst\",\"User.findFirstOrThrow\",\"User.findMany\",\"data\",\"User.createOne\",\"User.createMany\",\"User.updateOne\",\"User.updateMany\",\"create\",\"update\",\"User.upsertOne\",\"User.deleteOne\",\"User.deleteMany\",\"having\",\"_avg\",\"_sum\",\"_min\",\"_max\",\"User.groupBy\",\"User.aggregate\",\"Article.findUnique\",\"Article.findUniqueOrThrow\",\"Article.findFirst\",\"Article.findFirstOrThrow\",\"Article.findMany\",\"Article.createOne\",\"Article.createMany\",\"Article.updateOne\",\"Article.updateMany\",\"Article.upsertOne\",\"Article.deleteOne\",\"Article.deleteMany\",\"Article.groupBy\",\"Article.aggregate\",\"Strategy.findUnique\",\"Strategy.findUniqueOrThrow\",\"Strategy.findFirst\",\"Strategy.findFirstOrThrow\",\"Strategy.findMany\",\"Strategy.createOne\",\"Strategy.createMany\",\"Strategy.updateOne\",\"Strategy.updateMany\",\"Strategy.upsertOne\",\"Strategy.deleteOne\",\"Strategy.deleteMany\",\"Strategy.groupBy\",\"Strategy.aggregate\",\"indicator\",\"points\",\"Indicator.findUnique\",\"Indicator.findUniqueOrThrow\",\"Indicator.findFirst\",\"Indicator.findFirstOrThrow\",\"Indicator.findMany\",\"Indicator.createOne\",\"Indicator.createMany\",\"Indicator.updateOne\",\"Indicator.updateMany\",\"Indicator.upsertOne\",\"Indicator.deleteOne\",\"Indicator.deleteMany\",\"Indicator.groupBy\",\"Indicator.aggregate\",\"IndicatorPoint.findUnique\",\"IndicatorPoint.findUniqueOrThrow\",\"IndicatorPoint.findFirst\",\"IndicatorPoint.findFirstOrThrow\",\"IndicatorPoint.findMany\",\"IndicatorPoint.createOne\",\"IndicatorPoint.createMany\",\"IndicatorPoint.updateOne\",\"IndicatorPoint.updateMany\",\"IndicatorPoint.upsertOne\",\"IndicatorPoint.deleteOne\",\"IndicatorPoint.deleteMany\",\"IndicatorPoint.groupBy\",\"IndicatorPoint.aggregate\",\"News.findUnique\",\"News.findUniqueOrThrow\",\"News.findFirst\",\"News.findFirstOrThrow\",\"News.findMany\",\"News.createOne\",\"News.createMany\",\"News.updateOne\",\"News.updateMany\",\"News.upsertOne\",\"News.deleteOne\",\"News.deleteMany\",\"News.groupBy\",\"News.aggregate\",\"Alert.findUnique\",\"Alert.findUniqueOrThrow\",\"Alert.findFirst\",\"Alert.findFirstOrThrow\",\"Alert.findMany\",\"Alert.createOne\",\"Alert.createMany\",\"Alert.updateOne\",\"Alert.updateMany\",\"Alert.upsertOne\",\"Alert.deleteOne\",\"Alert.deleteMany\",\"Alert.groupBy\",\"Alert.aggregate\",\"ServerNode.findUnique\",\"ServerNode.findUniqueOrThrow\",\"ServerNode.findFirst\",\"ServerNode.findFirstOrThrow\",\"ServerNode.findMany\",\"ServerNode.createOne\",\"ServerNode.createMany\",\"ServerNode.updateOne\",\"ServerNode.updateMany\",\"ServerNode.upsertOne\",\"ServerNode.deleteOne\",\"ServerNode.deleteMany\",\"ServerNode.groupBy\",\"ServerNode.aggregate\",\"ServerNodeHistory.findUnique\",\"ServerNodeHistory.findUniqueOrThrow\",\"ServerNodeHistory.findFirst\",\"ServerNodeHistory.findFirstOrThrow\",\"ServerNodeHistory.findMany\",\"ServerNodeHistory.createOne\",\"ServerNodeHistory.createMany\",\"ServerNodeHistory.updateOne\",\"ServerNodeHistory.updateMany\",\"ServerNodeHistory.upsertOne\",\"ServerNodeHistory.deleteOne\",\"ServerNodeHistory.deleteMany\",\"ServerNodeHistory.groupBy\",\"ServerNodeHistory.aggregate\",\"ServiceCheck.findUnique\",\"ServiceCheck.findUniqueOrThrow\",\"ServiceCheck.findFirst\",\"ServiceCheck.findFirstOrThrow\",\"ServiceCheck.findMany\",\"ServiceCheck.createOne\",\"ServiceCheck.createMany\",\"ServiceCheck.updateOne\",\"ServiceCheck.updateMany\",\"ServiceCheck.upsertOne\",\"ServiceCheck.deleteOne\",\"ServiceCheck.deleteMany\",\"ServiceCheck.groupBy\",\"ServiceCheck.aggregate\",\"Econ_SowInventory.findUnique\",\"Econ_SowInventory.findUniqueOrThrow\",\"Econ_SowInventory.findFirst\",\"Econ_SowInventory.findFirstOrThrow\",\"Econ_SowInventory.findMany\",\"Econ_SowInventory.createOne\",\"Econ_SowInventory.createMany\",\"Econ_SowInventory.updateOne\",\"Econ_SowInventory.updateMany\",\"Econ_SowInventory.upsertOne\",\"Econ_SowInventory.deleteOne\",\"Econ_SowInventory.deleteMany\",\"Econ_SowInventory.groupBy\",\"Econ_SowInventory.aggregate\",\"ScraperCache.findUnique\",\"ScraperCache.findUniqueOrThrow\",\"ScraperCache.findFirst\",\"ScraperCache.findFirstOrThrow\",\"ScraperCache.findMany\",\"ScraperCache.createOne\",\"ScraperCache.createMany\",\"ScraperCache.updateOne\",\"ScraperCache.updateMany\",\"ScraperCache.upsertOne\",\"ScraperCache.deleteOne\",\"ScraperCache.deleteMany\",\"ScraperCache.groupBy\",\"ScraperCache.aggregate\",\"OilPriceHistory.findUnique\",\"OilPriceHistory.findUniqueOrThrow\",\"OilPriceHistory.findFirst\",\"OilPriceHistory.findFirstOrThrow\",\"OilPriceHistory.findMany\",\"OilPriceHistory.createOne\",\"OilPriceHistory.createMany\",\"OilPriceHistory.updateOne\",\"OilPriceHistory.updateMany\",\"OilPriceHistory.upsertOne\",\"OilPriceHistory.deleteOne\",\"OilPriceHistory.deleteMany\",\"OilPriceHistory.groupBy\",\"OilPriceHistory.aggregate\",\"MihomeAccount.findUnique\",\"MihomeAccount.findUniqueOrThrow\",\"MihomeAccount.findFirst\",\"MihomeAccount.findFirstOrThrow\",\"MihomeAccount.findMany\",\"MihomeAccount.createOne\",\"MihomeAccount.createMany\",\"MihomeAccount.updateOne\",\"MihomeAccount.updateMany\",\"MihomeAccount.upsertOne\",\"MihomeAccount.deleteOne\",\"MihomeAccount.deleteMany\",\"MihomeAccount.groupBy\",\"MihomeAccount.aggregate\",\"home\",\"device\",\"samples\",\"hours\",\"devices\",\"MihomeHome.findUnique\",\"MihomeHome.findUniqueOrThrow\",\"MihomeHome.findFirst\",\"MihomeHome.findFirstOrThrow\",\"MihomeHome.findMany\",\"MihomeHome.createOne\",\"MihomeHome.createMany\",\"MihomeHome.updateOne\",\"MihomeHome.updateMany\",\"MihomeHome.upsertOne\",\"MihomeHome.deleteOne\",\"MihomeHome.deleteMany\",\"MihomeHome.groupBy\",\"MihomeHome.aggregate\",\"MihomeDevice.findUnique\",\"MihomeDevice.findUniqueOrThrow\",\"MihomeDevice.findFirst\",\"MihomeDevice.findFirstOrThrow\",\"MihomeDevice.findMany\",\"MihomeDevice.createOne\",\"MihomeDevice.createMany\",\"MihomeDevice.updateOne\",\"MihomeDevice.updateMany\",\"MihomeDevice.upsertOne\",\"MihomeDevice.deleteOne\",\"MihomeDevice.deleteMany\",\"MihomeDevice.groupBy\",\"MihomeDevice.aggregate\",\"MihomeEnvSample.findUnique\",\"MihomeEnvSample.findUniqueOrThrow\",\"MihomeEnvSample.findFirst\",\"MihomeEnvSample.findFirstOrThrow\",\"MihomeEnvSample.findMany\",\"MihomeEnvSample.createOne\",\"MihomeEnvSample.createMany\",\"MihomeEnvSample.updateOne\",\"MihomeEnvSample.updateMany\",\"MihomeEnvSample.upsertOne\",\"MihomeEnvSample.deleteOne\",\"MihomeEnvSample.deleteMany\",\"MihomeEnvSample.groupBy\",\"MihomeEnvSample.aggregate\",\"MihomeEnvHour.findUnique\",\"MihomeEnvHour.findUniqueOrThrow\",\"MihomeEnvHour.findFirst\",\"MihomeEnvHour.findFirstOrThrow\",\"MihomeEnvHour.findMany\",\"MihomeEnvHour.createOne\",\"MihomeEnvHour.createMany\",\"MihomeEnvHour.updateOne\",\"MihomeEnvHour.updateMany\",\"MihomeEnvHour.upsertOne\",\"MihomeEnvHour.deleteOne\",\"MihomeEnvHour.deleteMany\",\"MihomeEnvHour.groupBy\",\"MihomeEnvHour.aggregate\",\"Habit.findUnique\",\"Habit.findUniqueOrThrow\",\"Habit.findFirst\",\"Habit.findFirstOrThrow\",\"Habit.findMany\",\"Habit.createOne\",\"Habit.createMany\",\"Habit.updateOne\",\"Habit.updateMany\",\"Habit.upsertOne\",\"Habit.deleteOne\",\"Habit.deleteMany\",\"Habit.groupBy\",\"Habit.aggregate\",\"CheckIn.findUnique\",\"CheckIn.findUniqueOrThrow\",\"CheckIn.findFirst\",\"CheckIn.findFirstOrThrow\",\"CheckIn.findMany\",\"CheckIn.createOne\",\"CheckIn.createMany\",\"CheckIn.updateOne\",\"CheckIn.updateMany\",\"CheckIn.upsertOne\",\"CheckIn.deleteOne\",\"CheckIn.deleteMany\",\"CheckIn.groupBy\",\"CheckIn.aggregate\",\"CheckList.findUnique\",\"CheckList.findUniqueOrThrow\",\"CheckList.findFirst\",\"CheckList.findFirstOrThrow\",\"CheckList.findMany\",\"CheckList.createOne\",\"CheckList.createMany\",\"CheckList.updateOne\",\"CheckList.updateMany\",\"CheckList.upsertOne\",\"CheckList.deleteOne\",\"CheckList.deleteMany\",\"CheckList.groupBy\",\"CheckList.aggregate\",\"CheckListItem.findUnique\",\"CheckListItem.findUniqueOrThrow\",\"CheckListItem.findFirst\",\"CheckListItem.findFirstOrThrow\",\"CheckListItem.findMany\",\"CheckListItem.createOne\",\"CheckListItem.createMany\",\"CheckListItem.updateOne\",\"CheckListItem.updateMany\",\"CheckListItem.upsertOne\",\"CheckListItem.deleteOne\",\"CheckListItem.deleteMany\",\"CheckListItem.groupBy\",\"CheckListItem.aggregate\",\"CheckListTemplate.findUnique\",\"CheckListTemplate.findUniqueOrThrow\",\"CheckListTemplate.findFirst\",\"CheckListTemplate.findFirstOrThrow\",\"CheckListTemplate.findMany\",\"CheckListTemplate.createOne\",\"CheckListTemplate.createMany\",\"CheckListTemplate.updateOne\",\"CheckListTemplate.updateMany\",\"CheckListTemplate.upsertOne\",\"CheckListTemplate.deleteOne\",\"CheckListTemplate.deleteMany\",\"CheckListTemplate.groupBy\",\"CheckListTemplate.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"userId\",\"name\",\"scene\",\"builtin\",\"createdAt\",\"updatedAt\",\"equals\",\"in\",\"notIn\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"string_contains\",\"string_starts_with\",\"string_ends_with\",\"array_starts_with\",\"array_ends_with\",\"array_contains\",\"contains\",\"startsWith\",\"endsWith\",\"search\",\"listId\",\"title\",\"note\",\"done\",\"doneAt\",\"sortOrder\",\"archived\",\"habitId\",\"date\",\"seq\",\"source\",\"period\",\"target\",\"color\",\"icon\",\"deviceId\",\"hour\",\"temperatureAvg\",\"humidityAvg\",\"raw\",\"temperature\",\"humidity\",\"temperatureMin\",\"temperatureMax\",\"humidityMin\",\"humidityMax\",\"origin\",\"sampledAt\",\"did\",\"homeId\",\"model\",\"alias\",\"tempSiid\",\"tempPiid\",\"humSiid\",\"humPiid\",\"battSiid\",\"battPiid\",\"battery\",\"batteryAt\",\"enabled\",\"lastSyncAt\",\"every\",\"some\",\"none\",\"deviceId_hour\",\"deviceId_date\",\"key\",\"status\",\"lastError\",\"province\",\"fuel\",\"price\",\"date_province_fuel\",\"scraperName\",\"lastDataHash\",\"lastUpdateAt\",\"metadata\",\"month\",\"inventory\",\"url\",\"latencyMs\",\"message\",\"lastCheckAt\",\"uuid\",\"cpu\",\"ram\",\"load\",\"disk\",\"netIn\",\"netOut\",\"process\",\"connections\",\"uuid_period\",\"cpuName\",\"os\",\"region\",\"memTotal\",\"diskTotal\",\"cpuCores\",\"cpuUsage\",\"ramUsed\",\"swapUsed\",\"load1\",\"load5\",\"load15\",\"diskUsed\",\"uptime\",\"recordedAt\",\"indicatorName\",\"valueA\",\"valueB\",\"sourceA\",\"sourceB\",\"diff\",\"diffRatio\",\"summary\",\"content\",\"publishedAt\",\"impactRank\",\"title_publishedAt\",\"indicatorId\",\"value\",\"groupName\",\"unit\",\"frequency\",\"hidden\",\"indicatorId_period\",\"description\",\"slug\",\"email\",\"password\",\"habitId_date_seq\",\"is\",\"isNot\",\"connectOrCreate\",\"upsert\",\"createMany\",\"set\",\"disconnect\",\"delete\",\"connect\",\"updateMany\",\"deleteMany\",\"_relevance\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"), + graph: "lQi_AcICDQcAAIsFACAIAACMBQAgCwAAjQUAIAwAAI4FACDeAgAAigUAMN8CAAAaABDgAgAAigUAMOECAgAAAAHjAgEAzwQAIeYCQADQBAAh5wJAANAEACHoAwEAAAAB6QMBAM8EACEBAAAAAQAgEQMAAJQFACAFAACMBQAg3gIAAJkFADDfAgAAAwAQ4AIAAJkFADDhAgIAzgQAIeICAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh_AIBAM8EACH_AgIAzgQAIYADIADdBAAhhQMBAM8EACGGAwIAzgQAIYcDAQDPBAAhiAMBAM8EACEDAwAA0QcAIAUAAM0HACD2AwAA2QcAIBEDAACUBQAgBQAAjAUAIN4CAACZBQAw3wIAAAMAEOACAACZBQAw4QICAAAAAeICAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh_AIBAM8EACH_AgIAzgQAIYADIADdBAAhhQMBAM8EACGGAwIAzgQAIYcDAQDPBAAhiAMBAM8EACEDAAAAAwAgAQAABAAwAgAABQAgDQMAAJQFACAEAACYBQAg3gIAAJcFADDfAgAABwAQ4AIAAJcFADDhAgIAzgQAIeICAgDOBAAh5gJAANAEACH8AgEAzwQAIYEDAgDOBAAhggMBAM8EACGDAwIAzgQAIYQDAQDPBAAhAwMAANEHACAEAADXBwAg9gMAANgHACAOAwAAlAUAIAQAAJgFACDeAgAAlwUAMN8CAAAHABDgAgAAlwUAMOECAgAAAAHiAgIAzgQAIeYCQADQBAAh_AIBAM8EACGBAwIAzgQAIYIDAQDPBAAhgwMCAM4EACGEAwEAzwQAIeoDAACWBQAgAwAAAAcAIAEAAAgAMAIAAAkAIAEAAAAHACADAAAABwAgAQAACAAwAgAACQAgDQMAAJQFACAKAACVBQAg3gIAAJMFADDfAgAADQAQ4AIAAJMFADDhAgIAzgQAIeICAgDOBAAh5AIBAM8EACHmAkAA0AQAIecCQADQBAAh-wIBAM8EACH8AgEAzwQAIYADIADdBAAhAwMAANEHACAKAADVBwAg9gMAANYHACANAwAAlAUAIAoAAJUFACDeAgAAkwUAMN8CAAANABDgAgAAkwUAMOECAgAAAAHiAgIAzgQAIeQCAQDPBAAh5gJAANAEACHnAkAA0AQAIfsCAQDPBAAh_AIBAM8EACGAAyAA3QQAIQMAAAANACABAAAOADACAAAPACANCQAAkgUAIN4CAACRBQAw3wIAABEAEOACAACRBQAw4QICAM4EACHmAkAA0AQAIecCQADQBAAh-gICAM4EACH7AgEAzwQAIfwCAQDPBAAh_QIgAN0EACH-AkAA2QQAIf8CAgDOBAAhAwkAANMHACD-AgAAmgUAIPYDAADUBwAgDQkAAJIFACDeAgAAkQUAMN8CAAARABDgAgAAkQUAMOECAgAAAAHmAkAA0AQAIecCQADQBAAh-gICAM4EACH7AgEAzwQAIfwCAQDPBAAh_QIgAN0EACH-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_wQAMN8CAABqABDgAgAA_wQAMOECAgAAAAHjAgEAAAAB5gJAANAEACHnAkAA0AQAIfsCAQDPBAAhhAMBAM8EACHhAwEAzwQAIeIDAQDPBAAh4wMBAM8EACHkAyAA3QQAIQEAAABiACAKPwAAgwUAIN4CAACCBQAw3wIAAGQAEOACAACCBQAw4QICAM4EACHmAkAA0AQAIecCQADQBAAhhQMBAM8EACHfAwIAzgQAIeADCADnBAAhAj8AAIQHACD2AwAAhQcAIAs_AACDBQAg3gIAAIIFADDfAgAAZAAQ4AIAAIIFADDhAgIAAAAB5gJAANAEACHnAkAA0AQAIYUDAQDPBAAh3wMCAM4EACHgAwgA5wQAIeUDAACBBQAgAwAAAGQAIAEAAGUAMAIAAGYAIAEAAABkACABAAAAYgAgDkAAAIAFACDeAgAA_wQAMN8CAABqABDgAgAA_wQAMOECAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh-wIBAM8EACGEAwEAzwQAIeEDAQDPBAAh4gMBAM8EACHjAwEAzwQAIeQDIADdBAAhAkAAAIIHACD2AwAAgwcAIAMAAABqACABAABrADACAABiACADAAAAagAgAQAAawAwAgAAYgAgAwAAAGoAIAEAAGsAMAIAAGIAIAtAAACBBwAg4QICAAAAAeMCAQAAAAHmAkAAAAAB5wJAAAAAAfsCAQAAAAGEAwEAAAAB4QMBAAAAAeIDAQAAAAHjAwEAAAAB5AMgAAAAAQESAABvACAK4QICAAAAAeMCAQAAAAHmAkAAAAAB5wJAAAAAAfsCAQAAAAGEAwEAAAAB4QMBAAAAAeIDAQAAAAHjAwEAAAAB5AMgAAAAAQESAABxADALQAAA9AYAIOECAgCjBQAh4wIBAKAFACHmAkAAogUAIecCQACiBQAh-wIBAKAFACGEAwEAoAUAIeEDAQCgBQAh4gMBAKAFACHjAwEAoAUAIeQDIAChBQAhAgAAAGIAIBIAAHMAIArhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfsCAQCgBQAhhAMBAKAFACHhAwEAoAUAIeIDAQCgBQAh4wMBAKAFACHkAyAAoQUAIQIAAABqACASAAB1ACADAAAAYgAgFwAAbwAgGAAAcwAgAQAAAGIAIAEAAABqACAFBgAA7wYAIB0AAPAGACAeAADzBgAgHwAA8gYAICAAAPEGACAN3gIAAP4EADDfAgAAewAQ4AIAAP4EADDhAgIAqAQAIeMCAQCqBAAh5gJAAK0EACHnAkAArQQAIfsCAQCqBAAhhAMBAKoEACHhAwEAqgQAIeIDAQCqBAAh4wMBAKoEACHkAyAArAQAIQMAAABqACABAAB6ADAcAAB7ACADAAAAagAgAQAAawAwAgAAYgAgAQAAAGYAIAEAAABmACADAAAAZAAgAQAAZQAwAgAAZgAgAwAAAGQAIAEAAGUAMAIAAGYAIAMAAABkACABAABlADACAABmACAHPwAA7gYAIOECAgAAAAHmAkAAAAAB5wJAAAAAAYUDAQAAAAHfAwIAAAAB4AMIAAAAAQESAACDAQAgBuECAgAAAAHmAkAAAAAB5wJAAAAAAYUDAQAAAAHfAwIAAAAB4AMIAAAAAQESAACFAQAwBz8AAO0GACDhAgIAowUAIeYCQACiBQAh5wJAAKIFACGFAwEAoAUAId8DAgCjBQAh4AMIALsGACECAAAAZgAgEgAAhwEAIAbhAgIAowUAIeYCQACiBQAh5wJAAKIFACGFAwEAoAUAId8DAgCjBQAh4AMIALsGACECAAAAZAAgEgAAiQEAIAMAAABmACAXAACDAQAgGAAAhwEAIAEAAABmACABAAAAZAAgBQYAAOgGACAdAADpBgAgHgAA7AYAIB8AAOsGACAgAADqBgAgCd4CAAD9BAAw3wIAAI8BABDgAgAA_QQAMOECAgCoBAAh5gJAAK0EACHnAkAArQQAIYUDAQCqBAAh3wMCAKgEACHgAwgA5AQAIQMAAABkACABAACOAQAwHAAAjwEAIAMAAABkACABAABlADACAABmACAN3gIAAPsEADDfAgAAlQEAEOACAAD7BAAw4QICAAAAAeYCQADQBAAh-wIBAM8EACGEAwEAzwQAIbYDAQDPBAAh2gMBAM8EACHbAwEA2wQAIdwDQADQBAAh3QMCAM4EACHeAwAA_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_AwQA9AQAIcADBAD0BAAhwQMCAM4EACHCAwIAzgQAIcQDAQDPBAAhxQMBAM8EACHGAwEAzwQAIccDBAD0BAAhyAMEAPQEACHJAwIAzgQAIcoDCADnBAAhywMEAPQEACHMAwQA9AQAIc0DCADnBAAhzgMIAOcEACHPAwgA5wQAIdADBAD0BAAh0QMEAPQEACHSA0AA0AQAIQH2AwAA2wYAIAMAAADDAQAgAQAAxAEAMAIAAMABACADAAAAwwEAIAEAAMQBADACAADAAQAgAwAAAMMBACABAADEAQAwAgAAwAEAIBnhAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAABqgMBAAAAAboDAQAAAAG_AwQAAAABwAMEAAAAAcEDAgAAAAHCAwIAAAABxAMBAAAAAcUDAQAAAAHGAwEAAAABxwMEAAAAAcgDBAAAAAHJAwIAAAABygMIAAAAAcsDBAAAAAHMAwQAAAABzQMIAAAAAc4DCAAAAAHPAwgAAAAB0AMEAAAAAdEDBAAAAAHSA0AAAAABARIAAMgBACAZ4QICAAAAAeMCAQAAAAHmAkAAAAAB5wJAAAAAAaoDAQAAAAG6AwEAAAABvwMEAAAAAcADBAAAAAHBAwIAAAABwgMCAAAAAcQDAQAAAAHFAwEAAAABxgMBAAAAAccDBAAAAAHIAwQAAAAByQMCAAAAAcoDCAAAAAHLAwQAAAABzAMEAAAAAc0DCAAAAAHOAwgAAAABzwMIAAAAAdADBAAAAAHRAwQAAAAB0gNAAAAAAQESAADKAQAwGeECAgCjBQAh4wIBAKAFACHmAkAAogUAIecCQACiBQAhqgMBAKAFACG6AwEAoAUAIb8DBADUBgAhwAMEANQGACHBAwIAowUAIcIDAgCjBQAhxAMBAKAFACHFAwEAoAUAIcYDAQCgBQAhxwMEANQGACHIAwQA1AYAIckDAgCjBQAhygMIALsGACHLAwQA1AYAIcwDBADUBgAhzQMIALsGACHOAwgAuwYAIc8DCAC7BgAh0AMEANQGACHRAwQA1AYAIdIDQACiBQAhAgAAAMABACASAADMAQAgGeECAgCjBQAh4wIBAKAFACHmAkAAogUAIecCQACiBQAhqgMBAKAFACG6AwEAoAUAIb8DBADUBgAhwAMEANQGACHBAwIAowUAIcIDAgCjBQAhxAMBAKAFACHFAwEAoAUAIcYDAQCgBQAhxwMEANQGACHIAwQA1AYAIckDAgCjBQAhygMIALsGACHLAwQA1AYAIcwDBADUBgAhzQMIALsGACHOAwgAuwYAIc8DCAC7BgAh0AMEANQGACHRAwQA1AYAIdIDQACiBQAhAgAAAMMBACASAADOAQAgAwAAAMABACAXAADIAQAgGAAAzAEAIAEAAADAAQAgAQAAAMMBACAFBgAA1gYAIB0AANcGACAeAADaBgAgHwAA2QYAICAAANgGACAc3gIAAPYEADDfAgAA1AEAEOACAAD2BAAw4QICAKgEACHjAgEAqgQAIeYCQACtBAAh5wJAAK0EACGqAwEAqgQAIboDAQCqBAAhvwMEAPAEACHAAwQA8AQAIcEDAgCoBAAhwgMCAKgEACHEAwEAqgQAIcUDAQCqBAAhxgMBAKoEACHHAwQA8AQAIcgDBADwBAAhyQMCAKgEACHKAwgA5AQAIcsDBADwBAAhzAMEAPAEACHNAwgA5AQAIc4DCADkBAAhzwMIAOQEACHQAwQA8AQAIdEDBADwBAAh0gNAAK0EACEDAAAAwwEAIAEAANMBADAcAADUAQAgAwAAAMMBACABAADEAQAwAgAAwAEAIBDeAgAA8wQAMN8CAADaAQAQ4AIAAPMEADDhAgIAAAAB5gJAANAEACGFA0AA0AQAIboDAQDPBAAhuwMIAOcEACG8AwQA9AQAIb0DCADnBAAhvgMEAPQEACG_AwQA9AQAIcADBAD0BAAhwQMCAM4EACHCAwIAzgQAIcMDAAD1BAAgAQAAANcBACABAAAA1wEAIA_eAgAA8wQAMN8CAADaAQAQ4AIAAPMEADDhAgIAzgQAIeYCQADQBAAhhQNAANAEACG6AwEAzwQAIbsDCADnBAAhvAMEAPQEACG9AwgA5wQAIb4DBAD0BAAhvwMEAPQEACHAAwQA9AQAIcEDAgDOBAAhwgMCAM4EACEB9gMAANUGACADAAAA2gEAIAEAANsBADACAADXAQAgAwAAANoBACABAADbAQAwAgAA1wEAIAMAAADaAQAgAQAA2wEAMAIAANcBACAM4QICAAAAAeYCQAAAAAGFA0AAAAABugMBAAAAAbsDCAAAAAG8AwQAAAABvQMIAAAAAb4DBAAAAAG_AwQAAAABwAMEAAAAAcEDAgAAAAHCAwIAAAABARIAAN8BACAM4QICAAAAAeYCQAAAAAGFA0AAAAABugMBAAAAAbsDCAAAAAG8AwQAAAABvQMIAAAAAb4DBAAAAAG_AwQAAAABwAMEAAAAAcEDAgAAAAHCAwIAAAABARIAAOEBADAM4QICAKMFACHmAkAAogUAIYUDQACiBQAhugMBAKAFACG7AwgAuwYAIbwDBADUBgAhvQMIALsGACG-AwQA1AYAIb8DBADUBgAhwAMEANQGACHBAwIAowUAIcIDAgCjBQAhAgAAANcBACASAADjAQAgDOECAgCjBQAh5gJAAKIFACGFA0AAogUAIboDAQCgBQAhuwMIALsGACG8AwQA1AYAIb0DCAC7BgAhvgMEANQGACG_AwQA1AYAIcADBADUBgAhwQMCAKMFACHCAwIAowUAIQIAAADaAQAgEgAA5QEAIAMAAADXAQAgFwAA3wEAIBgAAOMBACABAAAA1wEAIAEAAADaAQAgBQYAAM8GACAdAADQBgAgHgAA0wYAIB8AANIGACAgAADRBgAgD94CAADvBAAw3wIAAOsBABDgAgAA7wQAMOECAgCoBAAh5gJAAK0EACGFA0AArQQAIboDAQCqBAAhuwMIAOQEACG8AwQA8AQAIb0DCADkBAAhvgMEAPAEACG_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-BgAgHgAAwQYAIB8AAMAGACAgAAC_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_hAgIAowUAIeYCQACiBQAhggNAAKIFACGJAwIAowUAIYsDCADnBQAhjAMIAOcFACGNA4AAAAABjgMIAOcFACGPAwgA5wUAIZADCADnBQAhkQMIAOcFACGSAwgA5wUAIZMDCADnBQAhlAMBAKAFACGVA0AArAUAIQIAAADnAgAgEgAApgMAIAMAAADpAgAgFwAAoAMAIBgAAKQDACABAAAA6QIAIAEAAADnAgAgDwYAAOoFACAdAADrBQAgHgAA7gUAIB8AAO0FACAgAADsBQAgiwMAAJoFACCMAwAAmgUAII0DAACaBQAgjgMAAJoFACCPAwAAmgUAIJADAACaBQAgkQMAAJoFACCSAwAAmgUAIJMDAACaBQAglQMAAJoFACAS3gIAAMcEADDfAgAArAMAEOACAADHBAAw4QICAKgEACHmAkAArQQAIYIDQACtBAAhiQMCAKgEACGLAwgAwwQAIYwDCADDBAAhjQMAAMQEACCOAwgAwwQAIY8DCADDBAAhkAMIAMMEACGRAwgAwwQAIZIDCADDBAAhkwMIAMMEACGUAwEAqgQAIZUDQAC8BAAhAwAAAOcCACABAACrAwAwHAAArAMAIAMAAADnAgAgAQAA6AIAMAIAAOkCACABAAAA7QIAIAEAAADtAgAgAwAAAOsCACABAADsAgAwAgAA7QIAIAMAAADrAgAgAQAA7AIAMAIAAO0CACADAAAA6wIAIAEAAOwCADACAADtAgAgCNwBAADpBQAg4QICAAAAAeYCQAAAAAGJAwIAAAABigNAAAAAAYsDCAAAAAGMAwgAAAABjQOAAAAAAQESAAC0AwAgB-ECAgAAAAHmAkAAAAABiQMCAAAAAYoDQAAAAAGLAwgAAAABjAMIAAAAAY0DgAAAAAEBEgAAtgMAMAjcAQAA6AUAIOECAgCjBQAh5gJAAKIFACGJAwIAowUAIYoDQACiBQAhiwMIAOcFACGMAwgA5wUAIY0DgAAAAAECAAAA7QIAIBIAALgDACAH4QICAKMFACHmAkAAogUAIYkDAgCjBQAhigNAAKIFACGLAwgA5wUAIYwDCADnBQAhjQOAAAAAAQIAAADrAgAgEgAAugMAIAMAAADtAgAgFwAAtAMAIBgAALgDACABAAAA7QIAIAEAAADrAgAgCAYAAOIFACAdAADjBQAgHgAA5gUAIB8AAOUFACAgAADkBQAgiwMAAJoFACCMAwAAmgUAII0DAACaBQAgCt4CAADCBAAw3wIAAMADABDgAgAAwgQAMOECAgCoBAAh5gJAAK0EACGJAwIAqAQAIYoDQACtBAAhiwMIAMMEACGMAwgAwwQAIY0DAADEBAAgAwAAAOsCACABAAC_AwAwHAAAwAMAIAMAAADrAgAgAQAA7AIAMAIAAO0CACABAAAABQAgAQAAAAUAIAMAAAADACABAAAEADACAAAFACADAAAAAwAgAQAABAAwAgAABQAgAwAAAAMAIAEAAAQAMAIAAAUAIA4DAADgBQAgBQAA4QUAIOECAgAAAAHiAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAAB_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQESAADIAwAgDOECAgAAAAHiAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAAB_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQESAADKAwAwDgMAANIFACAFAADTBQAg4QICAKMFACHiAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfwCAQCgBQAh_wICAKMFACGAAyAAoQUAIYUDAQCgBQAhhgMCAKMFACGHAwEAoAUAIYgDAQCgBQAhAgAAAAUAIBIAAMwDACAM4QICAKMFACHiAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfwCAQCgBQAh_wICAKMFACGAAyAAoQUAIYUDAQCgBQAhhgMCAKMFACGHAwEAoAUAIYgDAQCgBQAhAgAAAAMAIBIAAM4DACADAAAABQAgFwAAyAMAIBgAAMwDACABAAAABQAgAQAAAAMAIAUGAADNBQAgHQAAzgUAIB4AANEFACAfAADQBQAgIAAAzwUAIA_eAgAAwQQAMN8CAADUAwAQ4AIAAMEEADDhAgIAqAQAIeICAgCoBAAh4wIBAKoEACHmAkAArQQAIecCQACtBAAh_AIBAKoEACH_AgIAqAQAIYADIACsBAAhhQMBAKoEACGGAwIAqAQAIYcDAQCqBAAhiAMBAKoEACEDAAAAAwAgAQAA0wMAMBwAANQDACADAAAAAwAgAQAABAAwAgAABQAgAQAAAAkAIAEAAAAJACADAAAABwAgAQAACAAwAgAACQAgAwAAAAcAIAEAAAgAMAIAAAkAIAMAAAAHACABAAAIADACAAAJACAKAwAAzAUAIAQAAMsFACDhAgIAAAAB4gICAAAAAeYCQAAAAAH8AgEAAAABgQMCAAAAAYIDAQAAAAGDAwIAAAABhAMBAAAAAQESAADcAwAgCOECAgAAAAHiAgIAAAAB5gJAAAAAAfwCAQAAAAGBAwIAAAABggMBAAAAAYMDAgAAAAGEAwEAAAABARIAAN4DADAKAwAAygUAIAQAAMkFACDhAgIAowUAIeICAgCjBQAh5gJAAKIFACH8AgEAoAUAIYEDAgCjBQAhggMBAKAFACGDAwIAowUAIYQDAQCgBQAhAgAAAAkAIBIAAOADACAI4QICAKMFACHiAgIAowUAIeYCQACiBQAh_AIBAKAFACGBAwIAowUAIYIDAQCgBQAhgwMCAKMFACGEAwEAoAUAIQIAAAAHACASAADiAwAgAwAAAAkAIBcAANwDACAYAADgAwAgAQAAAAkAIAEAAAAHACAFBgAAxAUAIB0AAMUFACAeAADIBQAgHwAAxwUAICAAAMYFACAL3gIAAMAEADDfAgAA6AMAEOACAADABAAw4QICAKgEACHiAgIAqAQAIeYCQACtBAAh_AIBAKoEACGBAwIAqAQAIYIDAQCqBAAhgwMCAKgEACGEAwEAqgQAIQMAAAAHACABAADnAwAwHAAA6AMAIAMAAAAHACABAAAIADACAAAJACABAAAADwAgAQAAAA8AIAMAAAANACABAAAOADACAAAPACADAAAADQAgAQAADgAwAgAADwAgAwAAAA0AIAEAAA4AMAIAAA8AIAoDAADCBQAgCgAAwwUAIOECAgAAAAHiAgIAAAAB5AIBAAAAAeYCQAAAAAHnAkAAAAAB-wIBAAAAAfwCAQAAAAGAAyAAAAABARIAAPADACAI4QICAAAAAeICAgAAAAHkAgEAAAAB5gJAAAAAAecCQAAAAAH7AgEAAAAB_AIBAAAAAYADIAAAAAEBEgAA8gMAMAoDAAC0BQAgCgAAtQUAIOECAgCjBQAh4gICAKMFACHkAgEAoAUAIeYCQACiBQAh5wJAAKIFACH7AgEAoAUAIfwCAQCgBQAhgAMgAKEFACECAAAADwAgEgAA9AMAIAjhAgIAowUAIeICAgCjBQAh5AIBAKAFACHmAkAAogUAIecCQACiBQAh-wIBAKAFACH8AgEAoAUAIYADIAChBQAhAgAAAA0AIBIAAPYDACADAAAADwAgFwAA8AMAIBgAAPQDACABAAAADwAgAQAAAA0AIAUGAACvBQAgHQAAsAUAIB4AALMFACAfAACyBQAgIAAAsQUAIAveAgAAvwQAMN8CAAD8AwAQ4AIAAL8EADDhAgIAqAQAIeICAgCoBAAh5AIBAKoEACHmAkAArQQAIecCQACtBAAh-wIBAKoEACH8AgEAqgQAIYADIACsBAAhAwAAAA0AIAEAAPsDADAcAAD8AwAgAwAAAA0AIAEAAA4AMAIAAA8AIAEAAAATACABAAAAEwAgAwAAABEAIAEAABIAMAIAABMAIAMAAAARACABAAASADACAAATACADAAAAEQAgAQAAEgAwAgAAEwAgCgkAAK4FACDhAgIAAAAB5gJAAAAAAecCQAAAAAH6AgIAAAAB-wIBAAAAAfwCAQAAAAH9AiAAAAAB_gJAAAAAAf8CAgAAAAEBEgAAhAQAIAnhAgIAAAAB5gJAAAAAAecCQAAAAAH6AgIAAAAB-wIBAAAAAfwCAQAAAAH9AiAAAAAB_gJAAAAAAf8CAgAAAAEBEgAAhgQAMAoJAACtBQAg4QICAKMFACHmAkAAogUAIecCQACiBQAh-gICAKMFACH7AgEAoAUAIfwCAQCgBQAh_QIgAKEFACH-AkAArAUAIf8CAgCjBQAhAgAAABMAIBIAAIgEACAJ4QICAKMFACHmAkAAogUAIecCQACiBQAh-gICAKMFACH7AgEAoAUAIfwCAQCgBQAh_QIgAKEFACH-AkAArAUAIf8CAgCjBQAhAgAAABEAIBIAAIoEACADAAAAEwAgFwAAhAQAIBgAAIgEACABAAAAEwAgAQAAABEAIAYGAACnBQAgHQAAqAUAIB4AAKsFACAfAACqBQAgIAAAqQUAIP4CAACaBQAgDN4CAAC7BAAw3wIAAJAEABDgAgAAuwQAMOECAgCoBAAh5gJAAK0EACHnAkAArQQAIfoCAgCoBAAh-wIBAKoEACH8AgEAqgQAIf0CIACsBAAh_gJAALwEACH_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_AIBAKoEACH9AiAArAQAIf4CQAC8BAAh_wICAKgEACELBgAAtwQAIB8AAL4EACAgAAC-BAAg6AJAAAAAAekCQAAAAAXqAkAAAAAF6wJAAAAAAewCQAAAAAHtAkAAAAAB7gJAAAAAAe8CQAC9BAAhCwYAALcEACAfAAC-BAAgIAAAvgQAIOgCQAAAAAHpAkAAAAAF6gJAAAAABesCQAAAAAHsAkAAAAAB7QJAAAAAAe4CQAAAAAHvAkAAvQQAIQjoAkAAAAAB6QJAAAAABeoCQAAAAAXrAkAAAAAB7AJAAAAAAe0CQAAAAAHuAkAAAAAB7wJAAL4EACEL3gIAAL8EADDfAgAA_AMAEOACAAC_BAAw4QICAKgEACHiAgIAqAQAIeQCAQCqBAAh5gJAAK0EACHnAkAArQQAIfsCAQCqBAAh_AIBAKoEACGAAyAArAQAIQveAgAAwAQAMN8CAADoAwAQ4AIAAMAEADDhAgIAqAQAIeICAgCoBAAh5gJAAK0EACH8AgEAqgQAIYEDAgCoBAAhggMBAKoEACGDAwIAqAQAIYQDAQCqBAAhD94CAADBBAAw3wIAANQDABDgAgAAwQQAMOECAgCoBAAh4gICAKgEACHjAgEAqgQAIeYCQACtBAAh5wJAAK0EACH8AgEAqgQAIf8CAgCoBAAhgAMgAKwEACGFAwEAqgQAIYYDAgCoBAAhhwMBAKoEACGIAwEAqgQAIQreAgAAwgQAMN8CAADAAwAQ4AIAAMIEADDhAgIAqAQAIeYCQACtBAAhiQMCAKgEACGKA0AArQQAIYsDCADDBAAhjAMIAMMEACGNAwAAxAQAIA0GAAC3BAAgHQAAuAQAIB4AALgEACAfAAC4BAAgIAAAuAQAIOgCCAAAAAHpAggAAAAF6gIIAAAABesCCAAAAAHsAggAAAAB7QIIAAAAAe4CCAAAAAHvAggAxgQAIQ8GAAC3BAAgHwAAxQQAICAAAMUEACDoAoAAAAAB6wKAAAAAAewCgAAAAAHtAoAAAAAB7gKAAAAAAe8CgAAAAAHwAgEAAAAB8QIBAAAAAfICAQAAAAHzAoAAAAAB9AKAAAAAAfUCgAAAAAEM6AKAAAAAAesCgAAAAAHsAoAAAAAB7QKAAAAAAe4CgAAAAAHvAoAAAAAB8AIBAAAAAfECAQAAAAHyAgEAAAAB8wKAAAAAAfQCgAAAAAH1AoAAAAABDQYAALcEACAdAAC4BAAgHgAAuAQAIB8AALgEACAgAAC4BAAg6AIIAAAAAekCCAAAAAXqAggAAAAF6wIIAAAAAewCCAAAAAHtAggAAAAB7gIIAAAAAe8CCADGBAAhEt4CAADHBAAw3wIAAKwDABDgAgAAxwQAMOECAgCoBAAh5gJAAK0EACGCA0AArQQAIYkDAgCoBAAhiwMIAMMEACGMAwgAwwQAIY0DAADEBAAgjgMIAMMEACGPAwgAwwQAIZADCADDBAAhkQMIAMMEACGSAwgAwwQAIZMDCADDBAAhlAMBAKoEACGVA0AAvAQAIRTeAgAAyAQAMN8CAACYAwAQ4AIAAMgEADDhAgIAqAQAIeMCAQCqBAAh5gJAAK0EACGWAwEAqgQAIZcDAQCqBAAhmAMBAKoEACGZAwEAyQQAIZoDAgCoBAAhmwMCAKgEACGcAwIAqAQAIZ0DAgCoBAAhngMCAKgEACGfAwIAqAQAIaADAgCpBAAhoQNAALwEACGiAyAArAQAIaMDQAC8BAAhDwYAALcEACAfAADLBAAgIAAAywQAIOgCAQAAAAHpAgEAAAAF6gIBAAAABesCAQAAAAHsAgEAAAAB7QIBAAAAAe4CAQAAAAHvAgEAygQAIfYCAQAAAAH3AgEAAAAB-AIBAAAAAfkCAQAAAAEPBgAAtwQAIB8AAMsEACAgAADLBAAg6AIBAAAAAekCAQAAAAXqAgEAAAAF6wIBAAAAAewCAQAAAAHtAgEAAAAB7gIBAAAAAe8CAQDKBAAh9gIBAAAAAfcCAQAAAAH4AgEAAAAB-QIBAAAAAQzoAgEAAAAB6QIBAAAABeoCAQAAAAXrAgEAAAAB7AIBAAAAAe0CAQAAAAHuAgEAAAAB7wIBAMsEACH2AgEAAAAB9wIBAAAAAfgCAQAAAAH5AgEAAAABB94CAADMBAAw3wIAAIQDABDgAgAAzAQAMOECAgCoBAAh4wIBAKoEACHmAkAArQQAIZcDAQCqBAAhCN8BAADRBAAg3gIAAM0EADDfAgAA8wIAEOACAADNBAAw4QICAM4EACHjAgEAzwQAIeYCQADQBAAhlwMBAM8EACEI6AICAAAAAekCAgAAAATqAgIAAAAE6wICAAAAAewCAgAAAAHtAgIAAAAB7gICAAAAAe8CAgCvBAAhDOgCAQAAAAHpAgEAAAAE6gIBAAAABOsCAQAAAAHsAgEAAAAB7QIBAAAAAe4CAQAAAAHvAgEAtQQAIfYCAQAAAAH3AgEAAAAB-AIBAAAAAfkCAQAAAAEI6AJAAAAAAekCQAAAAATqAkAAAAAE6wJAAAAAAewCQAAAAAHtAkAAAAAB7gJAAAAAAe8CQACwBAAhA6QDAADjAgAgpQMAAOMCACCmAwAA4wIAIAKJAwIAAAABigNAAAAAAQvcAQAA1gQAIN4CAADTBAAw3wIAAOsCABDgAgAA0wQAMOECAgDOBAAh5gJAANAEACGJAwIAzgQAIYoDQADQBAAhiwMIANQEACGMAwgA1AQAIY0DAADVBAAgCOgCCAAAAAHpAggAAAAF6gIIAAAABesCCAAAAAHsAggAAAAB7QIIAAAAAe4CCAAAAAHvAggAuAQAIQzoAoAAAAAB6wKAAAAAAewCgAAAAAHtAoAAAAAB7gKAAAAAAe8CgAAAAAHwAgEAAAAB8QIBAAAAAfICAQAAAAHzAoAAAAAB9AKAAAAAAfUCgAAAAAEZ2wEAAN4EACDdAQAA3wQAIN4BAADgBAAg3gIAANoEADDfAgAA4wIAEOACAADaBAAw4QICAM4EACHjAgEAzwQAIeYCQADQBAAhlgMBAM8EACGXAwEAzwQAIZgDAQDPBAAhmQMBANsEACGaAwIAzgQAIZsDAgDOBAAhnAMCAM4EACGdAwIAzgQAIZ4DAgDOBAAhnwMCAM4EACGgAwIA3AQAIaEDQADZBAAhogMgAN0EACGjA0AA2QQAIesDAADjAgAg7AMAAOMCACACggNAAAAAAYkDAgAAAAET3AEAANYEACDeAgAA2AQAMN8CAADnAgAQ4AIAANgEADDhAgIAzgQAIeYCQADQBAAhggNAANAEACGJAwIAzgQAIYsDCADUBAAhjAMIANQEACGNAwAA1QQAII4DCADUBAAhjwMIANQEACGQAwgA1AQAIZEDCADUBAAhkgMIANQEACGTAwgA1AQAIZQDAQDPBAAhlQNAANkEACEI6AJAAAAAAekCQAAAAAXqAkAAAAAF6wJAAAAAAewCQAAAAAHtAkAAAAAB7gJAAAAAAe8CQAC-BAAhF9sBAADeBAAg3QEAAN8EACDeAQAA4AQAIN4CAADaBAAw3wIAAOMCABDgAgAA2gQAMOECAgDOBAAh4wIBAM8EACHmAkAA0AQAIZYDAQDPBAAhlwMBAM8EACGYAwEAzwQAIZkDAQDbBAAhmgMCAM4EACGbAwIAzgQAIZwDAgDOBAAhnQMCAM4EACGeAwIAzgQAIZ8DAgDOBAAhoAMCANwEACGhA0AA2QQAIaIDIADdBAAhowNAANkEACEM6AIBAAAAAekCAQAAAAXqAgEAAAAF6wIBAAAAAewCAQAAAAHtAgEAAAAB7gIBAAAAAe8CAQDLBAAh9gIBAAAAAfcCAQAAAAH4AgEAAAAB-QIBAAAAAQjoAgIAAAAB6QICAAAABeoCAgAAAAXrAgIAAAAB7AICAAAAAe0CAgAAAAHuAgIAAAAB7wICALcEACEC6AIgAAAAAe8CIACyBAAhCt8BAADRBAAg3gIAAM0EADDfAgAA8wIAEOACAADNBAAw4QICAM4EACHjAgEAzwQAIeYCQADQBAAhlwMBAM8EACHrAwAA8wIAIOwDAADzAgAgA6QDAADnAgAgpQMAAOcCACCmAwAA5wIAIAOkAwAA6wIAIKUDAADrAgAgpgMAAOsCACAJ3gIAAOEEADDfAgAA3gIAEOACAADhBAAw4QICAKgEACHiAgEAqgQAIecCQACtBAAhqQMBAKoEACGqAwEAqgQAIasDAQCqBAAhCd4CAADiBAAw3wIAAM0CABDgAgAA4gQAMOECAgDOBAAh4gIBAM8EACHnAkAA0AQAIakDAQDPBAAhqgMBAM8EACGrAwEAzwQAIQreAgAA4wQAMN8CAADHAgAQ4AIAAOMEADDhAgIAqAQAIeYCQACtBAAhggNAAK0EACGEAwEAqgQAIawDAQCqBAAhrQMBAKoEACGuAwgA5AQAIQ0GAACvBAAgHQAAugQAIB4AALoEACAfAAC6BAAgIAAAugQAIOgCCAAAAAHpAggAAAAE6gIIAAAABOsCCAAAAAHsAggAAAAB7QIIAAAAAe4CCAAAAAHvAggA5QQAIQ0GAACvBAAgHQAAugQAIB4AALoEACAfAAC6BAAgIAAAugQAIOgCCAAAAAHpAggAAAAE6gIIAAAABOsCCAAAAAHsAggAAAAB7QIIAAAAAe4CCAAAAAHvAggA5QQAIQreAgAA5gQAMN8CAAC2AgAQ4AIAAOYEADDhAgIAzgQAIeYCQADQBAAhggNAANAEACGEAwEAzwQAIawDAQDPBAAhrQMBAM8EACGuAwgA5wQAIQjoAggAAAAB6QIIAAAABOoCCAAAAATrAggAAAAB7AIIAAAAAe0CCAAAAAHuAggAAAAB7wIIALoEACEDggNAAAAAAawDAQAAAAGtAwEAAAABCt4CAADpBAAw3wIAALACABDgAgAA6QQAMOECAgCoBAAh5gJAAK0EACHnAkAArQQAIbADAQCqBAAhsQMBAMkEACGyA0AArQQAIbMDAADEBAAgCt4CAADqBAAw3wIAAJ8CABDgAgAA6gQAMOECAgDOBAAh5gJAANAEACHnAkAA0AQAIbADAQDPBAAhsQMBANsEACGyA0AA0AQAIbMDAADVBAAgCN4CAADrBAAw3wIAAJkCABDgAgAA6wQAMOECAgCoBAAh5gJAAK0EACHnAkAArQQAIbQDAQCqBAAhtQMCAKgEACEI3gIAAOwEADDfAgAAiAIAEOACAADsBAAw4QICAM4EACHmAkAA0AQAIecCQADQBAAhtAMBAM8EACG1AwIAzgQAIQzeAgAA7QQAMN8CAACCAgAQ4AIAAO0EADDhAgIAqAQAIeMCAQCqBAAh5gJAAK0EACHnAkAArQQAIaoDAQCqBAAhtgMBAKoEACG3AwIAqAQAIbgDAQCqBAAhuQNAAK0EACEM3gIAAO4EADDfAgAA8QEAEOACAADuBAAw4QICAM4EACHjAgEAzwQAIeYCQADQBAAh5wJAANAEACGqAwEAzwQAIbYDAQDPBAAhtwMCAM4EACG4AwEAzwQAIbkDQADQBAAhD94CAADvBAAw3wIAAOsBABDgAgAA7wQAMOECAgCoBAAh5gJAAK0EACGFA0AArQQAIboDAQCqBAAhuwMIAOQEACG8AwQA8AQAIb0DCADkBAAhvgMEAPAEACG_AwQA8AQAIcADBADwBAAhwQMCAKgEACHCAwIAqAQAIQ0GAACvBAAgHQAAugQAIB4AAPIEACAfAADyBAAgIAAA8gQAIOgCBAAAAAHpAgQAAAAE6gIEAAAABOsCBAAAAAHsAgQAAAAB7QIEAAAAAe4CBAAAAAHvAgQA8QQAIQ0GAACvBAAgHQAAugQAIB4AAPIEACAfAADyBAAgIAAA8gQAIOgCBAAAAAHpAgQAAAAE6gIEAAAABOsCBAAAAAHsAgQAAAAB7QIEAAAAAe4CBAAAAAHvAgQA8QQAIQjoAgQAAAAB6QIEAAAABOoCBAAAAATrAgQAAAAB7AIEAAAAAe0CBAAAAAHuAgQAAAAB7wIEAPIEACEP3gIAAPMEADDfAgAA2gEAEOACAADzBAAw4QICAM4EACHmAkAA0AQAIYUDQADQBAAhugMBAM8EACG7AwgA5wQAIbwDBAD0BAAhvQMIAOcEACG-AwQA9AQAIb8DBAD0BAAhwAMEAPQEACHBAwIAzgQAIcIDAgDOBAAhCOgCBAAAAAHpAgQAAAAE6gIEAAAABOsCBAAAAAHsAgQAAAAB7QIEAAAAAe4CBAAAAAHvAgQA8gQAIQKFA0AAAAABugMBAAAAARzeAgAA9gQAMN8CAADUAQAQ4AIAAPYEADDhAgIAqAQAIeMCAQCqBAAh5gJAAK0EACHnAkAArQQAIaoDAQCqBAAhugMBAKoEACG_AwQA8AQAIcADBADwBAAhwQMCAKgEACHCAwIAqAQAIcQDAQCqBAAhxQMBAKoEACHGAwEAqgQAIccDBADwBAAhyAMEAPAEACHJAwIAqAQAIcoDCADkBAAhywMEAPAEACHMAwQA8AQAIc0DCADkBAAhzgMIAOQEACHPAwgA5AQAIdADBADwBAAh0QMEAPAEACHSA0AArQQAIRzeAgAA9wQAMN8CAADDAQAQ4AIAAPcEADDhAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIaoDAQDPBAAhugMBAM8EACG_AwQA9AQAIcADBAD0BAAhwQMCAM4EACHCAwIAzgQAIcQDAQDPBAAhxQMBAM8EACHGAwEAzwQAIccDBAD0BAAhyAMEAPQEACHJAwIAzgQAIcoDCADnBAAhywMEAPQEACHMAwQA9AQAIc0DCADnBAAhzgMIAOcEACHPAwgA5wQAIdADBAD0BAAh0QMEAPQEACHSA0AA0AQAIQ3eAgAA-AQAMN8CAAC9AQAQ4AIAAPgEADDhAgIAqAQAIeYCQACtBAAhhQMBAKoEACHTAwEAqgQAIdQDCADkBAAh1QMIAOQEACHWAwEAqgQAIdcDAQCqBAAh2AMIAOQEACHZAwgA5AQAIQ3eAgAA-QQAMN8CAACsAQAQ4AIAAPkEADDhAgIAzgQAIeYCQADQBAAhhQMBAM8EACHTAwEAzwQAIdQDCADnBAAh1QMIAOcEACHWAwEAzwQAIdcDAQDPBAAh2AMIAOcEACHZAwgA5wQAIQzeAgAA-gQAMN8CAACmAQAQ4AIAAPoEADDhAgIAqAQAIeYCQACtBAAh-wIBAKoEACGEAwEAqgQAIbYDAQCqBAAh2gMBAKoEACHbAwEAyQQAIdwDQACtBAAh3QMCAKgEACEM3gIAAPsEADDfAgAAlQEAEOACAAD7BAAw4QICAM4EACHmAkAA0AQAIfsCAQDPBAAhhAMBAM8EACG2AwEAzwQAIdoDAQDPBAAh2wMBANsEACHcA0AA0AQAId0DAgDOBAAhAvsCAQAAAAHcA0AAAAABCd4CAAD9BAAw3wIAAI8BABDgAgAA_QQAMOECAgCoBAAh5gJAAK0EACHnAkAArQQAIYUDAQCqBAAh3wMCAKgEACHgAwgA5AQAIQ3eAgAA_gQAMN8CAAB7ABDgAgAA_gQAMOECAgCoBAAh4wIBAKoEACHmAkAArQQAIecCQACtBAAh-wIBAKoEACGEAwEAqgQAIeEDAQCqBAAh4gMBAKoEACHjAwEAqgQAIeQDIACsBAAhDkAAAIAFACDeAgAA_wQAMN8CAABqABDgAgAA_wQAMOECAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh-wIBAM8EACGEAwEAzwQAIeEDAQDPBAAh4gMBAM8EACHjAwEAzwQAIeQDIADdBAAhA6QDAABkACClAwAAZAAgpgMAAGQAIAKFAwEAAAAB3wMCAAAAAQo_AACDBQAg3gIAAIIFADDfAgAAZAAQ4AIAAIIFADDhAgIAzgQAIeYCQADQBAAh5wJAANAEACGFAwEAzwQAId8DAgDOBAAh4AMIAOcEACEQQAAAgAUAIN4CAAD_BAAw3wIAAGoAEOACAAD_BAAw4QICAM4EACHjAgEAzwQAIeYCQADQBAAh5wJAANAEACH7AgEAzwQAIYQDAQDPBAAh4QMBAM8EACHiAwEAzwQAIeMDAQDPBAAh5AMgAN0EACHrAwAAagAg7AMAAGoAIAkSAACrBAAg3gIAAIQFADDfAgAAXwAQ4AIAAIQFADDhAgIAqAQAIeMCAQCqBAAh5gJAAK0EACHnAkAArQQAIeYDAQCqBAAhCRIAAIYFACDeAgAAhQUAMN8CAABOABDgAgAAhQUAMOECAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh5gMBAM8EACEM6AKAAAAAAesCgAAAAAHsAoAAAAAB7QKAAAAAAe4CgAAAAAHvAoAAAAAB8AIBAAAAAfECAQAAAAHyAgEAAAAB8wKAAAAAAfQCgAAAAAH1AoAAAAABCt4CAACHBQAw3wIAAEgAEOACAACHBQAw4QICAKgEACHmAkAArQQAIecCQACtBAAh-wIBAKoEACGCAwEAqgQAIdsDAQCqBAAh5wMBAKoEACEK3gIAAIgFADDfAgAANwAQ4AIAAIgFADDhAgIAzgQAIeYCQADQBAAh5wJAANAEACH7AgEAzwQAIYIDAQDPBAAh2wMBAM8EACHnAwEAzwQAIQneAgAAiQUAMN8CAAAxABDgAgAAiQUAMOECAgCoBAAh4wIBAKoEACHmAkAArQQAIecCQACtBAAh6AMBAKoEACHpAwEAqgQAIQ0HAACLBQAgCAAAjAUAIAsAAI0FACAMAACOBQAg3gIAAIoFADDfAgAAGgAQ4AIAAIoFADDhAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIegDAQDPBAAh6QMBAM8EACEDpAMAAAMAIKUDAAADACCmAwAAAwAgA6QDAAAHACClAwAABwAgpgMAAAcAIAOkAwAADQAgpQMAAA0AIKYDAAANACADpAMAABYAIKUDAAAWACCmAwAAFgAgDAMAAJAFACAKAACGBQAg3gIAAI8FADDfAgAAFgAQ4AIAAI8FADDhAgIAzgQAIeICAgDcBAAh4wIBAM8EACHkAgEAzwQAIeUCIADdBAAh5gJAANAEACHnAkAA0AQAIQ8HAACLBQAgCAAAjAUAIAsAAI0FACAMAACOBQAg3gIAAIoFADDfAgAAGgAQ4AIAAIoFADDhAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIegDAQDPBAAh6QMBAM8EACHrAwAAGgAg7AMAABoAIA0JAACSBQAg3gIAAJEFADDfAgAAEQAQ4AIAAJEFADDhAgIAzgQAIeYCQADQBAAh5wJAANAEACH6AgIAzgQAIfsCAQDPBAAh_AIBAM8EACH9AiAA3QQAIf4CQADZBAAh_wICAM4EACEPAwAAlAUAIAoAAJUFACDeAgAAkwUAMN8CAAANABDgAgAAkwUAMOECAgDOBAAh4gICAM4EACHkAgEAzwQAIeYCQADQBAAh5wJAANAEACH7AgEAzwQAIfwCAQDPBAAhgAMgAN0EACHrAwAADQAg7AMAAA0AIA0DAACUBQAgCgAAlQUAIN4CAACTBQAw3wIAAA0AEOACAACTBQAw4QICAM4EACHiAgIAzgQAIeQCAQDPBAAh5gJAANAEACHnAkAA0AQAIfsCAQDPBAAh_AIBAM8EACGAAyAA3QQAIQ8HAACLBQAgCAAAjAUAIAsAAI0FACAMAACOBQAg3gIAAIoFADDfAgAAGgAQ4AIAAIoFADDhAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIegDAQDPBAAh6QMBAM8EACHrAwAAGgAg7AMAABoAIAOkAwAAEQAgpQMAABEAIKYDAAARACADgQMCAAAAAYIDAQAAAAGDAwIAAAABDQMAAJQFACAEAACYBQAg3gIAAJcFADDfAgAABwAQ4AIAAJcFADDhAgIAzgQAIeICAgDOBAAh5gJAANAEACH8AgEAzwQAIYEDAgDOBAAhggMBAM8EACGDAwIAzgQAIYQDAQDPBAAhEwMAAJQFACAFAACMBQAg3gIAAJkFADDfAgAAAwAQ4AIAAJkFADDhAgIAzgQAIeICAgDOBAAh4wIBAM8EACHmAkAA0AQAIecCQADQBAAh_AIBAM8EACH_AgIAzgQAIYADIADdBAAhhQMBAM8EACGGAwIAzgQAIYcDAQDPBAAhiAMBAM8EACHrAwAAAwAg7AMAAAMAIBEDAACUBQAgBQAAjAUAIN4CAACZBQAw3wIAAAMAEOACAACZBQAw4QICAM4EACHiAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIfwCAQDPBAAh_wICAM4EACGAAyAA3QQAIYUDAQDPBAAhhgMCAM4EACGHAwEAzwQAIYgDAQDPBAAhAAAAAAAAAfADAQAAAAEB8AMgAAAAAQHwA0AAAAABBfADAgAAAAH3AwIAAAAB-AMCAAAAAfkDAgAAAAH6AwIAAAABBfADAgAAAAH3AwIAAAAB-AMCAAAAAfkDAgAAAAH6AwIAAAABBxcAAJEIACAYAACUCAAg7QMAAJIIACDuAwAAkwgAIPEDAAAaACDyAwAAGgAg8wMAAAEAIAMXAACRCAAg7QMAAJIIACDzAwAAAQAgAAAAAAAB8ANAAAAAAQUXAACMCAAgGAAAjwgAIO0DAACNCAAg7gMAAI4IACDzAwAADwAgAxcAAIwIACDtAwAAjQgAIPMDAAAPACAAAAAAAAUXAACGCAAgGAAAiggAIO0DAACHCAAg7gMAAIkIACDzAwAAAQAgCxcAALYFADAYAAC7BQAw7QMAALcFADDuAwAAuAUAMO8DAAC5BQAg8AMAALoFADDxAwAAugUAMPIDAAC6BQAw8wMAALoFADD0AwAAvAUAMPUDAAC9BQAwCOECAgAAAAHmAkAAAAAB5wJAAAAAAfsCAQAAAAH8AgEAAAAB_QIgAAAAAf4CQAAAAAH_AgIAAAABAgAAABMAIBcAAMEFACADAAAAEwAgFwAAwQUAIBgAAMAFACABEgAAiAgAMA0JAACSBQAg3gIAAJEFADDfAgAAEQAQ4AIAAJEFADDhAgIAAAAB5gJAANAEACHnAkAA0AQAIfoCAgDOBAAh-wIBAM8EACH8AgEAzwQAIf0CIADdBAAh_gJAANkEACH_AgIAzgQAIQIAAAATACASAADABQAgAgAAAL4FACASAAC_BQAgDN4CAAC9BQAw3wIAAL4FABDgAgAAvQUAMOECAgDOBAAh5gJAANAEACHnAkAA0AQAIfoCAgDOBAAh-wIBAM8EACH8AgEAzwQAIf0CIADdBAAh_gJAANkEACH_AgIAzgQAIQzeAgAAvQUAMN8CAAC-BQAQ4AIAAL0FADDhAgIAzgQAIeYCQADQBAAh5wJAANAEACH6AgIAzgQAIfsCAQDPBAAh_AIBAM8EACH9AiAA3QQAIf4CQADZBAAh_wICAM4EACEI4QICAKMFACHmAkAAogUAIecCQACiBQAh-wIBAKAFACH8AgEAoAUAIf0CIAChBQAh_gJAAKwFACH_AgIAowUAIQjhAgIAowUAIeYCQACiBQAh5wJAAKIFACH7AgEAoAUAIfwCAQCgBQAh_QIgAKEFACH-AkAArAUAIf8CAgCjBQAhCOECAgAAAAHmAkAAAAAB5wJAAAAAAfsCAQAAAAH8AgEAAAAB_QIgAAAAAf4CQAAAAAH_AgIAAAABAxcAAIYIACDtAwAAhwgAIPMDAAABACAEFwAAtgUAMO0DAAC3BQAw7wMAALkFACDzAwAAugUAMAAAAAAABRcAAP4HACAYAACECAAg7QMAAP8HACDuAwAAgwgAIPMDAAAFACAFFwAA_AcAIBgAAIEIACDtAwAA_QcAIO4DAACACAAg8wMAAAEAIAMXAAD-BwAg7QMAAP8HACDzAwAABQAgAxcAAPwHACDtAwAA_QcAIPMDAAABACAAAAAAAAUXAAD2BwAgGAAA-gcAIO0DAAD3BwAg7gMAAPkHACDzAwAAAQAgCxcAANQFADAYAADZBQAw7QMAANUFADDuAwAA1gUAMO8DAADXBQAg8AMAANgFADDxAwAA2AUAMPIDAADYBQAw8wMAANgFADD0AwAA2gUAMPUDAADbBQAwCAMAAMwFACDhAgIAAAAB4gICAAAAAeYCQAAAAAH8AgEAAAABggMBAAAAAYMDAgAAAAGEAwEAAAABAgAAAAkAIBcAAN8FACADAAAACQAgFwAA3wUAIBgAAN4FACABEgAA-AcAMA4DAACUBQAgBAAAmAUAIN4CAACXBQAw3wIAAAcAEOACAACXBQAw4QICAAAAAeICAgDOBAAh5gJAANAEACH8AgEAzwQAIYEDAgDOBAAhggMBAM8EACGDAwIAzgQAIYQDAQDPBAAh6gMAAJYFACACAAAACQAgEgAA3gUAIAIAAADcBQAgEgAA3QUAIAveAgAA2wUAMN8CAADcBQAQ4AIAANsFADDhAgIAzgQAIeICAgDOBAAh5gJAANAEACH8AgEAzwQAIYEDAgDOBAAhggMBAM8EACGDAwIAzgQAIYQDAQDPBAAhC94CAADbBQAw3wIAANwFABDgAgAA2wUAMOECAgDOBAAh4gICAM4EACHmAkAA0AQAIfwCAQDPBAAhgQMCAM4EACGCAwEAzwQAIYMDAgDOBAAhhAMBAM8EACEH4QICAKMFACHiAgIAowUAIeYCQACiBQAh_AIBAKAFACGCAwEAoAUAIYMDAgCjBQAhhAMBAKAFACEIAwAAygUAIOECAgCjBQAh4gICAKMFACHmAkAAogUAIfwCAQCgBQAhggMBAKAFACGDAwIAowUAIYQDAQCgBQAhCAMAAMwFACDhAgIAAAAB4gICAAAAAeYCQAAAAAH8AgEAAAABggMBAAAAAYMDAgAAAAGEAwEAAAABAxcAAPYHACDtAwAA9wcAIPMDAAABACAEFwAA1AUAMO0DAADVBQAw7wMAANcFACDzAwAA2AUAMAAAAAAABfADCAAAAAH3AwgAAAAB-AMIAAAAAfkDCAAAAAH6AwgAAAABBRcAAPEHACAYAAD0BwAg7QMAAPIHACDuAwAA8wcAIPMDAADlAgAgAxcAAPEHACDtAwAA8gcAIPMDAADlAgAgAAAAAAAFFwAA7AcAIBgAAO8HACDtAwAA7QcAIO4DAADuBwAg8wMAAOUCACADFwAA7AcAIO0DAADtBwAg8wMAAOUCACAAAAAAAAHwAwEAAAABBRcAAOUHACAYAADqBwAg7QMAAOYHACDuAwAA6QcAIPMDAADhAgAgCxcAAIYGADAYAACLBgAw7QMAAIcGADDuAwAAiAYAMO8DAACJBgAg8AMAAIoGADDxAwAAigYAMPIDAACKBgAw8wMAAIoGADD0AwAAjAYAMPUDAACNBgAwCxcAAPoFADAYAAD_BQAw7QMAAPsFADDuAwAA_AUAMO8DAAD9BQAg8AMAAP4FADDxAwAA_gUAMPIDAAD-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-QIBAAAAAQAAAAAABfADCAAAAAH3AwgAAAAB-AMIAAAAAfkDCAAAAAH6AwgAAAABAfkCAQAAAAEAAAAAAAH5AgEAAAABAAAAAAAB-QIBAAAAAQAAAAAAAfkCAQAAAAEAAAAAAAXwAwQAAAAB9wMEAAAAAfgDBAAAAAH5AwQAAAAB-gMEAAAAAQH5AgEAAAABAAAAAAAB-QIBAAAAAQAAAAAAAfkCAQAAAAEAAAAAAAH5AgEAAAABAAAAAAAFFwAA3wcAIBgAAOIHACDtAwAA4AcAIO4DAADhBwAg8wMAAGIAIAMXAADfBwAg7QMAAOAHACDzAwAAYgAgAAAAAAALFwAA9QYAMBgAAPoGADDtAwAA9gYAMO4DAAD3BgAw7wMAAPgGACDwAwAA-QYAMPEDAAD5BgAw8gMAAPkGADDzAwAA-QYAMPQDAAD7BgAw9QMAAPwGADAF4QICAAAAAeYCQAAAAAHnAkAAAAABhQMBAAAAAeADCAAAAAECAAAAZgAgFwAAgAcAIAMAAABmACAXAACABwAgGAAA_wYAIAESAADeBwAwCz8AAIMFACDeAgAAggUAMN8CAABkABDgAgAAggUAMOECAgAAAAHmAkAA0AQAIecCQADQBAAhhQMBAM8EACHfAwIAzgQAIeADCADnBAAh5QMAAIEFACACAAAAZgAgEgAA_wYAIAIAAAD9BgAgEgAA_gYAIAneAgAA_AYAMN8CAAD9BgAQ4AIAAPwGADDhAgIAzgQAIeYCQADQBAAh5wJAANAEACGFAwEAzwQAId8DAgDOBAAh4AMIAOcEACEJ3gIAAPwGADDfAgAA_QYAEOACAAD8BgAw4QICAM4EACHmAkAA0AQAIecCQADQBAAhhQMBAM8EACHfAwIAzgQAIeADCADnBAAhBeECAgCjBQAh5gJAAKIFACHnAkAAogUAIYUDAQCgBQAh4AMIALsGACEF4QICAKMFACHmAkAAogUAIecCQACiBQAhhQMBAKAFACHgAwgAuwYAIQXhAgIAAAAB5gJAAAAAAecCQAAAAAGFAwEAAAAB4AMIAAAAAQQXAAD1BgAw7QMAAPYGADDvAwAA-AYAIPMDAAD5BgAwAAH5AgEAAAABAkAAAIIHACD2AwAAgwcAIAH5AgEAAAABAAAAAAAB-QIBAAAAAQAAAAAAAfkCAQAAAAEAAAAAAAsXAAC8BwAwGAAAwQcAMO0DAAC9BwAw7gMAAL4HADDvAwAAvwcAIPADAADABwAw8QMAAMAHADDyAwAAwAcAMPMDAADABwAw9AMAAMIHADD1AwAAwwcAMAsXAACzBwAwGAAAtwcAMO0DAAC0BwAw7gMAALUHADDvAwAAtgcAIPADAADYBQAw8QMAANgFADDyAwAA2AUAMPMDAADYBQAw9AMAALgHADD1AwAA2wUAMAsXAACnBwAwGAAArAcAMO0DAACoBwAw7gMAAKkHADDvAwAAqgcAIPADAACrBwAw8QMAAKsHADDyAwAAqwcAMPMDAACrBwAw9AMAAK0HADD1AwAArgcAMAsXAACbBwAwGAAAoAcAMO0DAACcBwAw7gMAAJ0HADDvAwAAngcAIPADAACfBwAw8QMAAJ8HADDyAwAAnwcAMPMDAACfBwAw9AMAAKEHADD1AwAAogcAMAcKgAAAAAHhAgIAAAAB4wIBAAAAAeQCAQAAAAHlAiAAAAAB5gJAAAAAAecCQAAAAAECAAAAGAAgFwAApgcAIAMAAAAYACAXAACmBwAgGAAApQcAIAESAADdBwAwDAMAAJAFACAKAACGBQAg3gIAAI8FADDfAgAAFgAQ4AIAAI8FADDhAgIAAAAB4gICANwEACHjAgEAzwQAIeQCAQDPBAAh5QIgAN0EACHmAkAA0AQAIecCQADQBAAhAgAAABgAIBIAAKUHACACAAAAowcAIBIAAKQHACALCgAAhgUAIN4CAACiBwAw3wIAAKMHABDgAgAAogcAMOECAgDOBAAh4gICANwEACHjAgEAzwQAIeQCAQDPBAAh5QIgAN0EACHmAkAA0AQAIecCQADQBAAhCwoAAIYFACDeAgAAogcAMN8CAACjBwAQ4AIAAKIHADDhAgIAzgQAIeICAgDcBAAh4wIBAM8EACHkAgEAzwQAIeUCIADdBAAh5gJAANAEACHnAkAA0AQAIQcKgAAAAAHhAgIAowUAIeMCAQCgBQAh5AIBAKAFACHlAiAAoQUAIeYCQACiBQAh5wJAAKIFACEHCoAAAAAB4QICAKMFACHjAgEAoAUAIeQCAQCgBQAh5QIgAKEFACHmAkAAogUAIecCQACiBQAhBwqAAAAAAeECAgAAAAHjAgEAAAAB5AIBAAAAAeUCIAAAAAHmAkAAAAAB5wJAAAAAAQgKAADDBQAg4QICAAAAAeQCAQAAAAHmAkAAAAAB5wJAAAAAAfsCAQAAAAH8AgEAAAABgAMgAAAAAQIAAAAPACAXAACyBwAgAwAAAA8AIBcAALIHACAYAACxBwAgARIAANwHADANAwAAlAUAIAoAAJUFACDeAgAAkwUAMN8CAAANABDgAgAAkwUAMOECAgAAAAHiAgIAzgQAIeQCAQDPBAAh5gJAANAEACHnAkAA0AQAIfsCAQDPBAAh_AIBAM8EACGAAyAA3QQAIQIAAAAPACASAACxBwAgAgAAAK8HACASAACwBwAgC94CAACuBwAw3wIAAK8HABDgAgAArgcAMOECAgDOBAAh4gICAM4EACHkAgEAzwQAIeYCQADQBAAh5wJAANAEACH7AgEAzwQAIfwCAQDPBAAhgAMgAN0EACEL3gIAAK4HADDfAgAArwcAEOACAACuBwAw4QICAM4EACHiAgIAzgQAIeQCAQDPBAAh5gJAANAEACHnAkAA0AQAIfsCAQDPBAAh_AIBAM8EACGAAyAA3QQAIQfhAgIAowUAIeQCAQCgBQAh5gJAAKIFACHnAkAAogUAIfsCAQCgBQAh_AIBAKAFACGAAyAAoQUAIQgKAAC1BQAg4QICAKMFACHkAgEAoAUAIeYCQACiBQAh5wJAAKIFACH7AgEAoAUAIfwCAQCgBQAhgAMgAKEFACEICgAAwwUAIOECAgAAAAHkAgEAAAAB5gJAAAAAAecCQAAAAAH7AgEAAAAB_AIBAAAAAYADIAAAAAEIBAAAywUAIOECAgAAAAHmAkAAAAAB_AIBAAAAAYEDAgAAAAGCAwEAAAABgwMCAAAAAYQDAQAAAAECAAAACQAgFwAAuwcAIAMAAAAJACAXAAC7BwAgGAAAugcAIAESAADbBwAwAgAAAAkAIBIAALoHACACAAAA3AUAIBIAALkHACAH4QICAKMFACHmAkAAogUAIfwCAQCgBQAhgQMCAKMFACGCAwEAoAUAIYMDAgCjBQAhhAMBAKAFACEIBAAAyQUAIOECAgCjBQAh5gJAAKIFACH8AgEAoAUAIYEDAgCjBQAhggMBAKAFACGDAwIAowUAIYQDAQCgBQAhCAQAAMsFACDhAgIAAAAB5gJAAAAAAfwCAQAAAAGBAwIAAAABggMBAAAAAYMDAgAAAAGEAwEAAAABDAUAAOEFACDhAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAAB_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQIAAAAFACAXAADHBwAgAwAAAAUAIBcAAMcHACAYAADGBwAgARIAANoHADARAwAAlAUAIAUAAIwFACDeAgAAmQUAMN8CAAADABDgAgAAmQUAMOECAgAAAAHiAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIfwCAQDPBAAh_wICAM4EACGAAyAA3QQAIYUDAQDPBAAhhgMCAM4EACGHAwEAzwQAIYgDAQDPBAAhAgAAAAUAIBIAAMYHACACAAAAxAcAIBIAAMUHACAP3gIAAMMHADDfAgAAxAcAEOACAADDBwAw4QICAM4EACHiAgIAzgQAIeMCAQDPBAAh5gJAANAEACHnAkAA0AQAIfwCAQDPBAAh_wICAM4EACGAAyAA3QQAIYUDAQDPBAAhhgMCAM4EACGHAwEAzwQAIYgDAQDPBAAhD94CAADDBwAw3wIAAMQHABDgAgAAwwcAMOECAgDOBAAh4gICAM4EACHjAgEAzwQAIeYCQADQBAAh5wJAANAEACH8AgEAzwQAIf8CAgDOBAAhgAMgAN0EACGFAwEAzwQAIYYDAgDOBAAhhwMBAM8EACGIAwEAzwQAIQvhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfwCAQCgBQAh_wICAKMFACGAAyAAoQUAIYUDAQCgBQAhhgMCAKMFACGHAwEAoAUAIYgDAQCgBQAhDAUAANMFACDhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfwCAQCgBQAh_wICAKMFACGAAyAAoQUAIYUDAQCgBQAhhgMCAKMFACGHAwEAoAUAIYgDAQCgBQAhDAUAAOEFACDhAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAAB_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQQXAAC8BwAw7QMAAL0HADDvAwAAvwcAIPMDAADABwAwBBcAALMHADDtAwAAtAcAMO8DAAC2BwAg8wMAANgFADAEFwAApwcAMO0DAACoBwAw7wMAAKoHACDzAwAAqwcAMAQXAACbBwAw7QMAAJwHADDvAwAAngcAIPMDAACfBwAwAAAAAAH5AgEAAAABBQcAAMwHACAIAADNBwAgCwAAzgcAIAwAAM8HACD2AwAA0AcAIAH5AgEAAAABAwMAANEHACAKAADVBwAg9gMAANYHACAB-QIBAAAAAQAB-QIBAAAAAQMDAADRBwAgBQAAzQcAIPYDAADZBwAgAfkCAQAAAAEB-QIBAAAAAQvhAgIAAAAB4wIBAAAAAeYCQAAAAAHnAkAAAAAB_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQfhAgIAAAAB5gJAAAAAAfwCAQAAAAGBAwIAAAABggMBAAAAAYMDAgAAAAGEAwEAAAABB-ECAgAAAAHkAgEAAAAB5gJAAAAAAecCQAAAAAH7AgEAAAAB_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_AIBAAAAAf8CAgAAAAGAAyAAAAABhQMBAAAAAYYDAgAAAAGHAwEAAAABiAMBAAAAAQIAAAAFACAXAAD-BwAgAwAAABoAIBcAAPwHACAYAACCCAAgCwAAABoAIAcAAJcHACALAACZBwAgDAAAmgcAIBIAAIIIACDhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIegDAQCgBQAh6QMBAKAFACEJBwAAlwcAIAsAAJkHACAMAACaBwAg4QICAKMFACHjAgEAoAUAIeYCQACiBQAh5wJAAKIFACHoAwEAoAUAIekDAQCgBQAhAwAAAAMAIBcAAP4HACAYAACFCAAgDwAAAAMAIAMAANIFACASAACFCAAg4QICAKMFACHiAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIfwCAQCgBQAh_wICAKMFACGAAyAAoQUAIYUDAQCgBQAhhgMCAKMFACGHAwEAoAUAIYgDAQCgBQAhDQMAANIFACDhAgIAowUAIeICAgCjBQAh4wIBAKAFACHmAkAAogUAIecCQACiBQAh_AIBAKAFACH_AgIAowUAIYADIAChBQAhhQMBAKAFACGGAwIAowUAIYcDAQCgBQAhiAMBAKAFACEJBwAAyAcAIAgAAMkHACAMAADLBwAg4QICAAAAAeMCAQAAAAHmAkAAAAAB5wJAAAAAAegDAQAAAAHpAwEAAAABAgAAAAEAIBcAAIYIACAI4QICAAAAAeYCQAAAAAHnAkAAAAAB-wIBAAAAAfwCAQAAAAH9AiAAAAAB_gJAAAAAAf8CAgAAAAEDAAAAGgAgFwAAhggAIBgAAIsIACALAAAAGgAgBwAAlwcAIAgAAJgHACAMAACaBwAgEgAAiwgAIOECAgCjBQAh4wIBAKAFACHmAkAAogUAIecCQACiBQAh6AMBAKAFACHpAwEAoAUAIQkHAACXBwAgCAAAmAcAIAwAAJoHACDhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIegDAQCgBQAh6QMBAKAFACEJAwAAwgUAIOECAgAAAAHiAgIAAAAB5AIBAAAAAeYCQAAAAAHnAkAAAAAB-wIBAAAAAfwCAQAAAAGAAyAAAAABAgAAAA8AIBcAAIwIACADAAAADQAgFwAAjAgAIBgAAJAIACALAAAADQAgAwAAtAUAIBIAAJAIACDhAgIAowUAIeICAgCjBQAh5AIBAKAFACHmAkAAogUAIecCQACiBQAh-wIBAKAFACH8AgEAoAUAIYADIAChBQAhCQMAALQFACDhAgIAowUAIeICAgCjBQAh5AIBAKAFACHmAkAAogUAIecCQACiBQAh-wIBAKAFACH8AgEAoAUAIYADIAChBQAhCQcAAMgHACAIAADJBwAgCwAAygcAIOECAgAAAAHjAgEAAAAB5gJAAAAAAecCQAAAAAHoAwEAAAAB6QMBAAAAAQIAAAABACAXAACRCAAgAwAAABoAIBcAAJEIACAYAACVCAAgCwAAABoAIAcAAJcHACAIAACYBwAgCwAAmQcAIBIAAJUIACDhAgIAowUAIeMCAQCgBQAh5gJAAKIFACHnAkAAogUAIegDAQCgBQAh6QMBAKAFACEJBwAAlwcAIAgAAJgHACALAACZBwAg4QICAKMFACHjAgEAoAUAIeYCQACiBQAh5wJAAKIFACHoAwEAoAUAIekDAQCgBQAhBQYACQcGAggMAwsQBQwZCAMDAAEFCgMGAAQCAwABBAACAQULAAMDAAEGAAcKFAYBCQAFAQoVAAEDGwEEBxwACB0ACx4ADB8AAAUGAAwdAA0eAA4fAA8gABAAAAAAAAUGAAwdAA0eAA4fAA8gABAABQYAFB0AFR4AFh8AFyAAGAAAAAAABQYAFB0AFR4AFh8AFyAAGAAFBgAcHQAdHgAeHwAfIAAgAAAAAAAFBgAcHQAdHgAeHwAfIAAgAgYAJEBnIwE_ACIBQGgABQYAJh0AJx4AKB8AKSAAKgAAAAAABQYAJh0AJx4AKB8AKSAAKgUGAC0dAC4eAC8fADAgADEAAAAAAAUGAC0dAC4eAC8fADAgADEABQYANR0ANh4ANx8AOCAAOQAAAAAABQYANR0ANh4ANx8AOCAAOQAFBgA9HQA-HgA_HwBAIABBAAAAAAAFBgA9HQA-HgA_HwBAIABBAAUGAEUdAEYeAEcfAEggAEkAAAAAAAUGAEUdAEYeAEcfAEggAEkABQYATR0ATh4ATx8AUCAAUQAAAAAABQYATR0ATh4ATx8AUCAAUQAFBgBVHQBWHgBXHwBYIABZAAAAAAAFBgBVHQBWHgBXHwBYIABZAAUGAF0dAF4eAF8fAGAgAGEAAAAAAAUGAF0dAF4eAF8fAGAgAGEABQYAZR0AZh4AZx8AaCAAaQAAAAAABQYAZR0AZh4AZx8AaCAAaQAFBgBtHQBuHgBvHwBwIABxAAAAAAAFBgBtHQBuHgBvHwBwIABxAAUGAHUdAHYeAHcfAHggAHkAAAAAAAUGAHUdAHYeAHcfAHggAHkCBgCAAd8B5gJ8BAYAf9sBAHvdAeoCfd4B7gJ-AdwBAHwB3AEAfALdAe8CAN4B8AIAAd8B8QIABQYAggEdAIMBHgCEAR8AhQEgAIYBAAAAAAAFBgCCAR0AgwEeAIQBHwCFASAAhgEFBgCJAR0AigEeAIsBHwCMASAAjQEAAAAAAAUGAIkBHQCKAR4AiwEfAIwBIACNAQUGAJABHQCRAR4AkgEfAJMBIACUAQAAAAAABQYAkAEdAJEBHgCSAR8AkwEgAJQBBQYAlwEdAJgBHgCZAR8AmgEgAJsBAAAAAAAFBgCXAR0AmAEeAJkBHwCaASAAmwEFBgCeAR0AnwEeAKABHwChASAAogEAAAAAAAUGAJ4BHQCfAR4AoAEfAKEBIACiAQUGAKUBHQCmAR4ApwEfAKgBIACpAQAAAAAABQYApQEdAKYBHgCnAR8AqAEgAKkBBQYArAEdAK0BHgCuAR8ArwEgALABAAAAAAAFBgCsAR0ArQEeAK4BHwCvASAAsAEFBgCzAR0AtAEeALUBHwC2ASAAtwEAAAAAAAUGALMBHQC0AR4AtQEfALYBIAC3AQUGALoBHQC7AR4AvAEfAL0BIAC-AQAAAAAABQYAugEdALsBHgC8AR8AvQEgAL4BDQIBDiABDyIBECMBESQBEyYBFCgKFSoBFiwKGS0BGi4BGy8KITILIjMRIzUSJDYSJTkSJjoSJzsSKD0SKT8KKkESK0MKLEQSLUUSLkYKL0kTMEoZMUwaMk0aM1AaNFEaNVIaNlQaN1YKOFgaOVoKOlsaO1waPF0KPWAbPmEhQWMiQmkiQ2wiRG0iRW4iRnAiR3IKSHQiSXYKSnciS3giTHkKTXwlTn0rT34jUH8jUYABI1KBASNTggEjVIQBI1WGAQpWiAEjV4oBCliLASNZjAEjWo0BCluQASxckQEyXZMBM16UATNflwEzYJgBM2GZATNimwEzY50BCmSfATNloQEKZqIBM2ejATNopAEKaacBNGqoATprqgE7bKsBO22uATturwE7b7ABO3CyATtxtAEKcrYBO3O4AQp0uQE7dboBO3a7AQp3vgE8eL8BQnnBAUN6wgFDe8UBQ3zGAUN9xwFDfskBQ3_LAQqAAc0BQ4EBzwEKggHQAUODAdEBQ4QB0gEKhQHVAUSGAdYBSocB2AFLiAHZAUuJAdwBS4oB3QFLiwHeAUuMAeABS40B4gEKjgHkAUuPAeYBCpAB5wFLkQHoAUuSAekBCpMB7AFMlAHtAVKVAe8BU5YB8AFTlwHzAVOYAfQBU5kB9QFTmgH3AVObAfkBCpwB-wFTnQH9AQqeAf4BU58B_wFToAGAAgqhAYMCVKIBhAJaowGGAlukAYcCW6UBigJbpgGLAlunAYwCW6gBjgJbqQGQAgqqAZICW6sBlAIKrAGVAlutAZYCW64BlwIKrwGaAlywAZsCYrEBnQJjsgGeAmOzAaECY7QBogJjtQGjAmO2AaUCY7cBpwIKuAGpAmO5AasCCroBrAJjuwGtAmO8Aa4CCr0BsQJkvgGyAmq_AbQCa8ABtQJrwQG4AmvCAbkCa8MBugJrxAG8AmvFAb4CCsYBwAJrxwHCAgrIAcMCa8kBxAJrygHFAgrLAcgCbMwByQJyzQHLAnPOAcwCc88BzwJz0AHQAnPRAdECc9IB0wJz0wHVAgrUAdcCc9UB2QIK1gHaAnPXAdsCc9gB3AIK2QHfAnTaAeACeuAB4gJ74QHyAnviAfUCe-MB9gJ75AH3AnvlAfkCe-YB-wIK5wH9AnvoAf8CCukBgAN76gGBA3vrAYIDCuwBhQOBAe0BhgOHAe4BhwN87wGIA3zwAYkDfPEBigN88gGLA3zzAY0DfPQBjwMK9QGRA3z2AZMDCvcBlAN8-AGVA3z5AZYDCvoBmQOIAfsBmgOOAfwBmwN9_QGcA33-AZ0Dff8BngN9gAKfA32BAqEDfYICowMKgwKlA32EAqcDCoUCqAN9hgKpA32HAqoDCogCrQOPAYkCrgOVAYoCrwN-iwKwA36MArEDfo0CsgN-jgKzA36PArUDfpACtwMKkQK5A36SArsDCpMCvAN-lAK9A36VAr4DCpYCwQOWAZcCwgOcAZgCwwMCmQLEAwKaAsUDApsCxgMCnALHAwKdAskDAp4CywMKnwLNAwKgAs8DCqEC0AMCogLRAwKjAtIDCqQC1QOdAaUC1gOjAaYC1wMDpwLYAwOoAtkDA6kC2gMDqgLbAwOrAt0DA6wC3wMKrQLhAwOuAuMDCq8C5AMDsALlAwOxAuYDCrIC6QOkAbMC6gOqAbQC6wMFtQLsAwW2Au0DBbcC7gMFuALvAwW5AvEDBboC8wMKuwL1AwW8AvcDCr0C-AMFvgL5AwW_AvoDCsAC_QOrAcEC_gOxAcIC_wMGwwKABAbEAoEEBsUCggQGxgKDBAbHAoUEBsgChwQKyQKJBAbKAosECssCjAQGzAKNBAbNAo4ECs4CkQSyAc8CkgS4AdACkwQI0QKUBAjSApUECNMClgQI1AKXBAjVApkECNYCmwQK1wKdBAjYAp8ECtkCoAQI2gKhBAjbAqIECtwCpQS5Ad0CpgS_AQ" } async function decodeBase64AsWasm(wasmBase64: string): Promise { @@ -367,6 +367,56 @@ export interface PrismaClient< * ``` */ get mihomeEnvHour(): Prisma.MihomeEnvHourDelegate; + + /** + * `prisma.habit`: Exposes CRUD operations for the **Habit** model. + * Example usage: + * ```ts + * // Fetch zero or more Habits + * const habits = await prisma.habit.findMany() + * ``` + */ + get habit(): Prisma.HabitDelegate; + + /** + * `prisma.checkIn`: Exposes CRUD operations for the **CheckIn** model. + * Example usage: + * ```ts + * // Fetch zero or more CheckIns + * const checkIns = await prisma.checkIn.findMany() + * ``` + */ + get checkIn(): Prisma.CheckInDelegate; + + /** + * `prisma.checkList`: Exposes CRUD operations for the **CheckList** model. + * Example usage: + * ```ts + * // Fetch zero or more CheckLists + * const checkLists = await prisma.checkList.findMany() + * ``` + */ + get checkList(): Prisma.CheckListDelegate; + + /** + * `prisma.checkListItem`: Exposes CRUD operations for the **CheckListItem** model. + * Example usage: + * ```ts + * // Fetch zero or more CheckListItems + * const checkListItems = await prisma.checkListItem.findMany() + * ``` + */ + get checkListItem(): Prisma.CheckListItemDelegate; + + /** + * `prisma.checkListTemplate`: Exposes CRUD operations for the **CheckListTemplate** model. + * Example usage: + * ```ts + * // Fetch zero or more CheckListTemplates + * const checkListTemplates = await prisma.checkListTemplate.findMany() + * ``` + */ + get checkListTemplate(): Prisma.CheckListTemplateDelegate; } export function getPrismaClientClass(): PrismaClientConstructor { diff --git a/generated/prisma/internal/prismaNamespace.ts b/generated/prisma/internal/prismaNamespace.ts index 2a1166a..26ef65d 100644 --- a/generated/prisma/internal/prismaNamespace.ts +++ b/generated/prisma/internal/prismaNamespace.ts @@ -401,7 +401,12 @@ export const ModelName = { MihomeHome: 'MihomeHome', MihomeDevice: 'MihomeDevice', MihomeEnvSample: 'MihomeEnvSample', - MihomeEnvHour: 'MihomeEnvHour' + MihomeEnvHour: 'MihomeEnvHour', + Habit: 'Habit', + CheckIn: 'CheckIn', + CheckList: 'CheckList', + CheckListItem: 'CheckListItem', + CheckListTemplate: 'CheckListTemplate' } as const export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -417,7 +422,7 @@ export type TypeMap + fields: Prisma.HabitFieldRefs + operations: { + findUnique: { + args: Prisma.HabitFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.HabitFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.HabitFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.HabitFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.HabitFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.HabitCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.HabitCreateManyArgs + result: BatchPayload + } + delete: { + args: Prisma.HabitDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.HabitUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.HabitDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.HabitUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.HabitUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.HabitAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.HabitGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.HabitCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CheckIn: { + payload: Prisma.$CheckInPayload + fields: Prisma.CheckInFieldRefs + operations: { + findUnique: { + args: Prisma.CheckInFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CheckInFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CheckInFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CheckInFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CheckInFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CheckInCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CheckInCreateManyArgs + result: BatchPayload + } + delete: { + args: Prisma.CheckInDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CheckInUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CheckInDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CheckInUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.CheckInUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CheckInAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CheckInGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CheckInCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CheckList: { + payload: Prisma.$CheckListPayload + fields: Prisma.CheckListFieldRefs + operations: { + findUnique: { + args: Prisma.CheckListFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CheckListFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CheckListFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CheckListFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CheckListFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CheckListCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CheckListCreateManyArgs + result: BatchPayload + } + delete: { + args: Prisma.CheckListDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CheckListUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CheckListDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CheckListUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.CheckListUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CheckListAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CheckListGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CheckListCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CheckListItem: { + payload: Prisma.$CheckListItemPayload + fields: Prisma.CheckListItemFieldRefs + operations: { + findUnique: { + args: Prisma.CheckListItemFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CheckListItemFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CheckListItemFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CheckListItemFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CheckListItemFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CheckListItemCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CheckListItemCreateManyArgs + result: BatchPayload + } + delete: { + args: Prisma.CheckListItemDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CheckListItemUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CheckListItemDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CheckListItemUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.CheckListItemUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CheckListItemAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CheckListItemGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CheckListItemCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CheckListTemplate: { + payload: Prisma.$CheckListTemplatePayload + fields: Prisma.CheckListTemplateFieldRefs + operations: { + findUnique: { + args: Prisma.CheckListTemplateFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CheckListTemplateFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CheckListTemplateFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CheckListTemplateFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CheckListTemplateFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CheckListTemplateCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CheckListTemplateCreateManyArgs + result: BatchPayload + } + delete: { + args: Prisma.CheckListTemplateDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CheckListTemplateUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CheckListTemplateDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CheckListTemplateUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.CheckListTemplateUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CheckListTemplateAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CheckListTemplateGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CheckListTemplateCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } } } & { other: { @@ -1924,6 +2259,81 @@ export const MihomeEnvHourScalarFieldEnum = { export type MihomeEnvHourScalarFieldEnum = (typeof MihomeEnvHourScalarFieldEnum)[keyof typeof MihomeEnvHourScalarFieldEnum] +export const HabitScalarFieldEnum = { + id: 'id', + userId: 'userId', + name: 'name', + period: 'period', + target: 'target', + color: 'color', + icon: 'icon', + note: 'note', + archived: 'archived', + sortOrder: 'sortOrder', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type HabitScalarFieldEnum = (typeof HabitScalarFieldEnum)[keyof typeof HabitScalarFieldEnum] + + +export const CheckInScalarFieldEnum = { + id: 'id', + habitId: 'habitId', + userId: 'userId', + date: 'date', + seq: 'seq', + note: 'note', + source: 'source', + createdAt: 'createdAt' +} as const + +export type CheckInScalarFieldEnum = (typeof CheckInScalarFieldEnum)[keyof typeof CheckInScalarFieldEnum] + + +export const CheckListScalarFieldEnum = { + id: 'id', + userId: 'userId', + title: 'title', + scene: 'scene', + note: 'note', + archived: 'archived', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListScalarFieldEnum = (typeof CheckListScalarFieldEnum)[keyof typeof CheckListScalarFieldEnum] + + +export const CheckListItemScalarFieldEnum = { + id: 'id', + listId: 'listId', + title: 'title', + note: 'note', + done: 'done', + doneAt: 'doneAt', + sortOrder: 'sortOrder', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListItemScalarFieldEnum = (typeof CheckListItemScalarFieldEnum)[keyof typeof CheckListItemScalarFieldEnum] + + +export const CheckListTemplateScalarFieldEnum = { + id: 'id', + userId: 'userId', + name: 'name', + scene: 'scene', + items: 'items', + builtin: 'builtin', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListTemplateScalarFieldEnum = (typeof CheckListTemplateScalarFieldEnum)[keyof typeof CheckListTemplateScalarFieldEnum] + + export const SortOrder = { asc: 'asc', desc: 'desc' @@ -2128,6 +2538,51 @@ export const MihomeEnvSampleOrderByRelevanceFieldEnum = { export type MihomeEnvSampleOrderByRelevanceFieldEnum = (typeof MihomeEnvSampleOrderByRelevanceFieldEnum)[keyof typeof MihomeEnvSampleOrderByRelevanceFieldEnum] +export const HabitOrderByRelevanceFieldEnum = { + name: 'name', + period: 'period', + color: 'color', + icon: 'icon', + note: 'note' +} as const + +export type HabitOrderByRelevanceFieldEnum = (typeof HabitOrderByRelevanceFieldEnum)[keyof typeof HabitOrderByRelevanceFieldEnum] + + +export const CheckInOrderByRelevanceFieldEnum = { + date: 'date', + note: 'note', + source: 'source' +} as const + +export type CheckInOrderByRelevanceFieldEnum = (typeof CheckInOrderByRelevanceFieldEnum)[keyof typeof CheckInOrderByRelevanceFieldEnum] + + +export const CheckListOrderByRelevanceFieldEnum = { + title: 'title', + scene: 'scene', + note: 'note' +} as const + +export type CheckListOrderByRelevanceFieldEnum = (typeof CheckListOrderByRelevanceFieldEnum)[keyof typeof CheckListOrderByRelevanceFieldEnum] + + +export const CheckListItemOrderByRelevanceFieldEnum = { + title: 'title', + note: 'note' +} as const + +export type CheckListItemOrderByRelevanceFieldEnum = (typeof CheckListItemOrderByRelevanceFieldEnum)[keyof typeof CheckListItemOrderByRelevanceFieldEnum] + + +export const CheckListTemplateOrderByRelevanceFieldEnum = { + name: 'name', + scene: 'scene' +} as const + +export type CheckListTemplateOrderByRelevanceFieldEnum = (typeof CheckListTemplateOrderByRelevanceFieldEnum)[keyof typeof CheckListTemplateOrderByRelevanceFieldEnum] + + /** * Field references @@ -2302,6 +2757,11 @@ export type GlobalOmitConfig = { mihomeDevice?: Prisma.MihomeDeviceOmit mihomeEnvSample?: Prisma.MihomeEnvSampleOmit mihomeEnvHour?: Prisma.MihomeEnvHourOmit + habit?: Prisma.HabitOmit + checkIn?: Prisma.CheckInOmit + checkList?: Prisma.CheckListOmit + checkListItem?: Prisma.CheckListItemOmit + checkListTemplate?: Prisma.CheckListTemplateOmit } /* Types for Logging */ diff --git a/generated/prisma/internal/prismaNamespaceBrowser.ts b/generated/prisma/internal/prismaNamespaceBrowser.ts index 96969ba..64b061e 100644 --- a/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -68,7 +68,12 @@ export const ModelName = { MihomeHome: 'MihomeHome', MihomeDevice: 'MihomeDevice', MihomeEnvSample: 'MihomeEnvSample', - MihomeEnvHour: 'MihomeEnvHour' + MihomeEnvHour: 'MihomeEnvHour', + Habit: 'Habit', + CheckIn: 'CheckIn', + CheckList: 'CheckList', + CheckListItem: 'CheckListItem', + CheckListTemplate: 'CheckListTemplate' } as const export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -363,6 +368,81 @@ export const MihomeEnvHourScalarFieldEnum = { export type MihomeEnvHourScalarFieldEnum = (typeof MihomeEnvHourScalarFieldEnum)[keyof typeof MihomeEnvHourScalarFieldEnum] +export const HabitScalarFieldEnum = { + id: 'id', + userId: 'userId', + name: 'name', + period: 'period', + target: 'target', + color: 'color', + icon: 'icon', + note: 'note', + archived: 'archived', + sortOrder: 'sortOrder', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type HabitScalarFieldEnum = (typeof HabitScalarFieldEnum)[keyof typeof HabitScalarFieldEnum] + + +export const CheckInScalarFieldEnum = { + id: 'id', + habitId: 'habitId', + userId: 'userId', + date: 'date', + seq: 'seq', + note: 'note', + source: 'source', + createdAt: 'createdAt' +} as const + +export type CheckInScalarFieldEnum = (typeof CheckInScalarFieldEnum)[keyof typeof CheckInScalarFieldEnum] + + +export const CheckListScalarFieldEnum = { + id: 'id', + userId: 'userId', + title: 'title', + scene: 'scene', + note: 'note', + archived: 'archived', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListScalarFieldEnum = (typeof CheckListScalarFieldEnum)[keyof typeof CheckListScalarFieldEnum] + + +export const CheckListItemScalarFieldEnum = { + id: 'id', + listId: 'listId', + title: 'title', + note: 'note', + done: 'done', + doneAt: 'doneAt', + sortOrder: 'sortOrder', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListItemScalarFieldEnum = (typeof CheckListItemScalarFieldEnum)[keyof typeof CheckListItemScalarFieldEnum] + + +export const CheckListTemplateScalarFieldEnum = { + id: 'id', + userId: 'userId', + name: 'name', + scene: 'scene', + items: 'items', + builtin: 'builtin', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CheckListTemplateScalarFieldEnum = (typeof CheckListTemplateScalarFieldEnum)[keyof typeof CheckListTemplateScalarFieldEnum] + + export const SortOrder = { asc: 'asc', desc: 'desc' @@ -566,3 +646,48 @@ export const MihomeEnvSampleOrderByRelevanceFieldEnum = { export type MihomeEnvSampleOrderByRelevanceFieldEnum = (typeof MihomeEnvSampleOrderByRelevanceFieldEnum)[keyof typeof MihomeEnvSampleOrderByRelevanceFieldEnum] + +export const HabitOrderByRelevanceFieldEnum = { + name: 'name', + period: 'period', + color: 'color', + icon: 'icon', + note: 'note' +} as const + +export type HabitOrderByRelevanceFieldEnum = (typeof HabitOrderByRelevanceFieldEnum)[keyof typeof HabitOrderByRelevanceFieldEnum] + + +export const CheckInOrderByRelevanceFieldEnum = { + date: 'date', + note: 'note', + source: 'source' +} as const + +export type CheckInOrderByRelevanceFieldEnum = (typeof CheckInOrderByRelevanceFieldEnum)[keyof typeof CheckInOrderByRelevanceFieldEnum] + + +export const CheckListOrderByRelevanceFieldEnum = { + title: 'title', + scene: 'scene', + note: 'note' +} as const + +export type CheckListOrderByRelevanceFieldEnum = (typeof CheckListOrderByRelevanceFieldEnum)[keyof typeof CheckListOrderByRelevanceFieldEnum] + + +export const CheckListItemOrderByRelevanceFieldEnum = { + title: 'title', + note: 'note' +} as const + +export type CheckListItemOrderByRelevanceFieldEnum = (typeof CheckListItemOrderByRelevanceFieldEnum)[keyof typeof CheckListItemOrderByRelevanceFieldEnum] + + +export const CheckListTemplateOrderByRelevanceFieldEnum = { + name: 'name', + scene: 'scene' +} as const + +export type CheckListTemplateOrderByRelevanceFieldEnum = (typeof CheckListTemplateOrderByRelevanceFieldEnum)[keyof typeof CheckListTemplateOrderByRelevanceFieldEnum] + diff --git a/generated/prisma/models.ts b/generated/prisma/models.ts index 05ae935..5cd3c70 100644 --- a/generated/prisma/models.ts +++ b/generated/prisma/models.ts @@ -26,4 +26,9 @@ export type * from './models/MihomeHome.ts' export type * from './models/MihomeDevice.ts' export type * from './models/MihomeEnvSample.ts' export type * from './models/MihomeEnvHour.ts' +export type * from './models/Habit.ts' +export type * from './models/CheckIn.ts' +export type * from './models/CheckList.ts' +export type * from './models/CheckListItem.ts' +export type * from './models/CheckListTemplate.ts' export type * from './commonInputTypes.ts' \ No newline at end of file diff --git a/generated/prisma/models/CheckIn.ts b/generated/prisma/models/CheckIn.ts new file mode 100644 index 0000000..afe0207 --- /dev/null +++ b/generated/prisma/models/CheckIn.ts @@ -0,0 +1,1518 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `CheckIn` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model CheckIn + * + */ +export type CheckInModel = runtime.Types.Result.DefaultSelection + +export type AggregateCheckIn = { + _count: CheckInCountAggregateOutputType | null + _avg: CheckInAvgAggregateOutputType | null + _sum: CheckInSumAggregateOutputType | null + _min: CheckInMinAggregateOutputType | null + _max: CheckInMaxAggregateOutputType | null +} + +export type CheckInAvgAggregateOutputType = { + id: number | null + habitId: number | null + userId: number | null + seq: number | null +} + +export type CheckInSumAggregateOutputType = { + id: number | null + habitId: number | null + userId: number | null + seq: number | null +} + +export type CheckInMinAggregateOutputType = { + id: number | null + habitId: number | null + userId: number | null + date: string | null + seq: number | null + note: string | null + source: string | null + createdAt: Date | null +} + +export type CheckInMaxAggregateOutputType = { + id: number | null + habitId: number | null + userId: number | null + date: string | null + seq: number | null + note: string | null + source: string | null + createdAt: Date | null +} + +export type CheckInCountAggregateOutputType = { + id: number + habitId: number + userId: number + date: number + seq: number + note: number + source: number + createdAt: number + _all: number +} + + +export type CheckInAvgAggregateInputType = { + id?: true + habitId?: true + userId?: true + seq?: true +} + +export type CheckInSumAggregateInputType = { + id?: true + habitId?: true + userId?: true + seq?: true +} + +export type CheckInMinAggregateInputType = { + id?: true + habitId?: true + userId?: true + date?: true + seq?: true + note?: true + source?: true + createdAt?: true +} + +export type CheckInMaxAggregateInputType = { + id?: true + habitId?: true + userId?: true + date?: true + seq?: true + note?: true + source?: true + createdAt?: true +} + +export type CheckInCountAggregateInputType = { + id?: true + habitId?: true + userId?: true + date?: true + seq?: true + note?: true + source?: true + createdAt?: true + _all?: true +} + +export type CheckInAggregateArgs = { + /** + * Filter which CheckIn to aggregate. + */ + where?: Prisma.CheckInWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckIns to fetch. + */ + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.CheckInWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckIns from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckIns. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CheckIns + **/ + _count?: true | CheckInCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CheckInAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CheckInSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CheckInMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CheckInMaxAggregateInputType +} + +export type GetCheckInAggregateType = { + [P in keyof T & keyof AggregateCheckIn]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type CheckInGroupByArgs = { + where?: Prisma.CheckInWhereInput + orderBy?: Prisma.CheckInOrderByWithAggregationInput | Prisma.CheckInOrderByWithAggregationInput[] + by: Prisma.CheckInScalarFieldEnum[] | Prisma.CheckInScalarFieldEnum + having?: Prisma.CheckInScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CheckInCountAggregateInputType | true + _avg?: CheckInAvgAggregateInputType + _sum?: CheckInSumAggregateInputType + _min?: CheckInMinAggregateInputType + _max?: CheckInMaxAggregateInputType +} + +export type CheckInGroupByOutputType = { + id: number + habitId: number + userId: number + date: string + seq: number + note: string + source: string + createdAt: Date + _count: CheckInCountAggregateOutputType | null + _avg: CheckInAvgAggregateOutputType | null + _sum: CheckInSumAggregateOutputType | null + _min: CheckInMinAggregateOutputType | null + _max: CheckInMaxAggregateOutputType | null +} + +export type GetCheckInGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof CheckInGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type CheckInWhereInput = { + AND?: Prisma.CheckInWhereInput | Prisma.CheckInWhereInput[] + OR?: Prisma.CheckInWhereInput[] + NOT?: Prisma.CheckInWhereInput | Prisma.CheckInWhereInput[] + id?: Prisma.IntFilter<"CheckIn"> | number + habitId?: Prisma.IntFilter<"CheckIn"> | number + userId?: Prisma.IntFilter<"CheckIn"> | number + date?: Prisma.StringFilter<"CheckIn"> | string + seq?: Prisma.IntFilter<"CheckIn"> | number + note?: Prisma.StringFilter<"CheckIn"> | string + source?: Prisma.StringFilter<"CheckIn"> | string + createdAt?: Prisma.DateTimeFilter<"CheckIn"> | Date | string + habit?: Prisma.XOR + user?: Prisma.XOR +} + +export type CheckInOrderByWithRelationInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + date?: Prisma.SortOrder + seq?: Prisma.SortOrder + note?: Prisma.SortOrder + source?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + habit?: Prisma.HabitOrderByWithRelationInput + user?: Prisma.UserOrderByWithRelationInput + _relevance?: Prisma.CheckInOrderByRelevanceInput +} + +export type CheckInWhereUniqueInput = Prisma.AtLeast<{ + id?: number + habitId_date_seq?: Prisma.CheckInHabitIdDateSeqCompoundUniqueInput + AND?: Prisma.CheckInWhereInput | Prisma.CheckInWhereInput[] + OR?: Prisma.CheckInWhereInput[] + NOT?: Prisma.CheckInWhereInput | Prisma.CheckInWhereInput[] + habitId?: Prisma.IntFilter<"CheckIn"> | number + userId?: Prisma.IntFilter<"CheckIn"> | number + date?: Prisma.StringFilter<"CheckIn"> | string + seq?: Prisma.IntFilter<"CheckIn"> | number + note?: Prisma.StringFilter<"CheckIn"> | string + source?: Prisma.StringFilter<"CheckIn"> | string + createdAt?: Prisma.DateTimeFilter<"CheckIn"> | Date | string + habit?: Prisma.XOR + user?: Prisma.XOR +}, "id" | "habitId_date_seq"> + +export type CheckInOrderByWithAggregationInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + date?: Prisma.SortOrder + seq?: Prisma.SortOrder + note?: Prisma.SortOrder + source?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + _count?: Prisma.CheckInCountOrderByAggregateInput + _avg?: Prisma.CheckInAvgOrderByAggregateInput + _max?: Prisma.CheckInMaxOrderByAggregateInput + _min?: Prisma.CheckInMinOrderByAggregateInput + _sum?: Prisma.CheckInSumOrderByAggregateInput +} + +export type CheckInScalarWhereWithAggregatesInput = { + AND?: Prisma.CheckInScalarWhereWithAggregatesInput | Prisma.CheckInScalarWhereWithAggregatesInput[] + OR?: Prisma.CheckInScalarWhereWithAggregatesInput[] + NOT?: Prisma.CheckInScalarWhereWithAggregatesInput | Prisma.CheckInScalarWhereWithAggregatesInput[] + id?: Prisma.IntWithAggregatesFilter<"CheckIn"> | number + habitId?: Prisma.IntWithAggregatesFilter<"CheckIn"> | number + userId?: Prisma.IntWithAggregatesFilter<"CheckIn"> | number + date?: Prisma.StringWithAggregatesFilter<"CheckIn"> | string + seq?: Prisma.IntWithAggregatesFilter<"CheckIn"> | number + note?: Prisma.StringWithAggregatesFilter<"CheckIn"> | string + source?: Prisma.StringWithAggregatesFilter<"CheckIn"> | string + createdAt?: Prisma.DateTimeWithAggregatesFilter<"CheckIn"> | Date | string +} + +export type CheckInCreateInput = { + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string + habit: Prisma.HabitCreateNestedOneWithoutRecordsInput + user: Prisma.UserCreateNestedOneWithoutCheckInsInput +} + +export type CheckInUncheckedCreateInput = { + id?: number + habitId: number + userId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInUpdateInput = { + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habit?: Prisma.HabitUpdateOneRequiredWithoutRecordsNestedInput + user?: Prisma.UserUpdateOneRequiredWithoutCheckInsNestedInput +} + +export type CheckInUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + habitId?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInCreateManyInput = { + id?: number + habitId: number + userId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInUpdateManyMutationInput = { + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + habitId?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInListRelationFilter = { + every?: Prisma.CheckInWhereInput + some?: Prisma.CheckInWhereInput + none?: Prisma.CheckInWhereInput +} + +export type CheckInOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type CheckInOrderByRelevanceInput = { + fields: Prisma.CheckInOrderByRelevanceFieldEnum | Prisma.CheckInOrderByRelevanceFieldEnum[] + sort: Prisma.SortOrder + search: string +} + +export type CheckInHabitIdDateSeqCompoundUniqueInput = { + habitId: number + date: string + seq: number +} + +export type CheckInCountOrderByAggregateInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + date?: Prisma.SortOrder + seq?: Prisma.SortOrder + note?: Prisma.SortOrder + source?: Prisma.SortOrder + createdAt?: Prisma.SortOrder +} + +export type CheckInAvgOrderByAggregateInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + seq?: Prisma.SortOrder +} + +export type CheckInMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + date?: Prisma.SortOrder + seq?: Prisma.SortOrder + note?: Prisma.SortOrder + source?: Prisma.SortOrder + createdAt?: Prisma.SortOrder +} + +export type CheckInMinOrderByAggregateInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + date?: Prisma.SortOrder + seq?: Prisma.SortOrder + note?: Prisma.SortOrder + source?: Prisma.SortOrder + createdAt?: Prisma.SortOrder +} + +export type CheckInSumOrderByAggregateInput = { + id?: Prisma.SortOrder + habitId?: Prisma.SortOrder + userId?: Prisma.SortOrder + seq?: Prisma.SortOrder +} + +export type CheckInCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutUserInput[] | Prisma.CheckInUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutUserInput | Prisma.CheckInCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckInCreateManyUserInputEnvelope + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] +} + +export type CheckInUncheckedCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutUserInput[] | Prisma.CheckInUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutUserInput | Prisma.CheckInCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckInCreateManyUserInputEnvelope + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] +} + +export type CheckInUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutUserInput[] | Prisma.CheckInUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutUserInput | Prisma.CheckInCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckInUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckInUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckInCreateManyUserInputEnvelope + set?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + disconnect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + delete?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + update?: Prisma.CheckInUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckInUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckInUpdateManyWithWhereWithoutUserInput | Prisma.CheckInUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] +} + +export type CheckInUncheckedUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutUserInput[] | Prisma.CheckInUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutUserInput | Prisma.CheckInCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckInUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckInUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckInCreateManyUserInputEnvelope + set?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + disconnect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + delete?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + update?: Prisma.CheckInUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckInUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckInUpdateManyWithWhereWithoutUserInput | Prisma.CheckInUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] +} + +export type CheckInCreateNestedManyWithoutHabitInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutHabitInput[] | Prisma.CheckInUncheckedCreateWithoutHabitInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutHabitInput | Prisma.CheckInCreateOrConnectWithoutHabitInput[] + createMany?: Prisma.CheckInCreateManyHabitInputEnvelope + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] +} + +export type CheckInUncheckedCreateNestedManyWithoutHabitInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutHabitInput[] | Prisma.CheckInUncheckedCreateWithoutHabitInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutHabitInput | Prisma.CheckInCreateOrConnectWithoutHabitInput[] + createMany?: Prisma.CheckInCreateManyHabitInputEnvelope + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] +} + +export type CheckInUpdateManyWithoutHabitNestedInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutHabitInput[] | Prisma.CheckInUncheckedCreateWithoutHabitInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutHabitInput | Prisma.CheckInCreateOrConnectWithoutHabitInput[] + upsert?: Prisma.CheckInUpsertWithWhereUniqueWithoutHabitInput | Prisma.CheckInUpsertWithWhereUniqueWithoutHabitInput[] + createMany?: Prisma.CheckInCreateManyHabitInputEnvelope + set?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + disconnect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + delete?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + update?: Prisma.CheckInUpdateWithWhereUniqueWithoutHabitInput | Prisma.CheckInUpdateWithWhereUniqueWithoutHabitInput[] + updateMany?: Prisma.CheckInUpdateManyWithWhereWithoutHabitInput | Prisma.CheckInUpdateManyWithWhereWithoutHabitInput[] + deleteMany?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] +} + +export type CheckInUncheckedUpdateManyWithoutHabitNestedInput = { + create?: Prisma.XOR | Prisma.CheckInCreateWithoutHabitInput[] | Prisma.CheckInUncheckedCreateWithoutHabitInput[] + connectOrCreate?: Prisma.CheckInCreateOrConnectWithoutHabitInput | Prisma.CheckInCreateOrConnectWithoutHabitInput[] + upsert?: Prisma.CheckInUpsertWithWhereUniqueWithoutHabitInput | Prisma.CheckInUpsertWithWhereUniqueWithoutHabitInput[] + createMany?: Prisma.CheckInCreateManyHabitInputEnvelope + set?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + disconnect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + delete?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + connect?: Prisma.CheckInWhereUniqueInput | Prisma.CheckInWhereUniqueInput[] + update?: Prisma.CheckInUpdateWithWhereUniqueWithoutHabitInput | Prisma.CheckInUpdateWithWhereUniqueWithoutHabitInput[] + updateMany?: Prisma.CheckInUpdateManyWithWhereWithoutHabitInput | Prisma.CheckInUpdateManyWithWhereWithoutHabitInput[] + deleteMany?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] +} + +export type CheckInCreateWithoutUserInput = { + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string + habit: Prisma.HabitCreateNestedOneWithoutRecordsInput +} + +export type CheckInUncheckedCreateWithoutUserInput = { + id?: number + habitId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInCreateOrConnectWithoutUserInput = { + where: Prisma.CheckInWhereUniqueInput + create: Prisma.XOR +} + +export type CheckInCreateManyUserInputEnvelope = { + data: Prisma.CheckInCreateManyUserInput | Prisma.CheckInCreateManyUserInput[] + skipDuplicates?: boolean +} + +export type CheckInUpsertWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckInWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CheckInUpdateWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckInWhereUniqueInput + data: Prisma.XOR +} + +export type CheckInUpdateManyWithWhereWithoutUserInput = { + where: Prisma.CheckInScalarWhereInput + data: Prisma.XOR +} + +export type CheckInScalarWhereInput = { + AND?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] + OR?: Prisma.CheckInScalarWhereInput[] + NOT?: Prisma.CheckInScalarWhereInput | Prisma.CheckInScalarWhereInput[] + id?: Prisma.IntFilter<"CheckIn"> | number + habitId?: Prisma.IntFilter<"CheckIn"> | number + userId?: Prisma.IntFilter<"CheckIn"> | number + date?: Prisma.StringFilter<"CheckIn"> | string + seq?: Prisma.IntFilter<"CheckIn"> | number + note?: Prisma.StringFilter<"CheckIn"> | string + source?: Prisma.StringFilter<"CheckIn"> | string + createdAt?: Prisma.DateTimeFilter<"CheckIn"> | Date | string +} + +export type CheckInCreateWithoutHabitInput = { + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string + user: Prisma.UserCreateNestedOneWithoutCheckInsInput +} + +export type CheckInUncheckedCreateWithoutHabitInput = { + id?: number + userId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInCreateOrConnectWithoutHabitInput = { + where: Prisma.CheckInWhereUniqueInput + create: Prisma.XOR +} + +export type CheckInCreateManyHabitInputEnvelope = { + data: Prisma.CheckInCreateManyHabitInput | Prisma.CheckInCreateManyHabitInput[] + skipDuplicates?: boolean +} + +export type CheckInUpsertWithWhereUniqueWithoutHabitInput = { + where: Prisma.CheckInWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CheckInUpdateWithWhereUniqueWithoutHabitInput = { + where: Prisma.CheckInWhereUniqueInput + data: Prisma.XOR +} + +export type CheckInUpdateManyWithWhereWithoutHabitInput = { + where: Prisma.CheckInScalarWhereInput + data: Prisma.XOR +} + +export type CheckInCreateManyUserInput = { + id?: number + habitId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInUpdateWithoutUserInput = { + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habit?: Prisma.HabitUpdateOneRequiredWithoutRecordsNestedInput +} + +export type CheckInUncheckedUpdateWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + habitId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInUncheckedUpdateManyWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + habitId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInCreateManyHabitInput = { + id?: number + userId: number + date: string + seq?: number + note?: string + source?: string + createdAt?: Date | string +} + +export type CheckInUpdateWithoutHabitInput = { + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneRequiredWithoutCheckInsNestedInput +} + +export type CheckInUncheckedUpdateWithoutHabitInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckInUncheckedUpdateManyWithoutHabitInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + date?: Prisma.StringFieldUpdateOperationsInput | string + seq?: Prisma.IntFieldUpdateOperationsInput | number + note?: Prisma.StringFieldUpdateOperationsInput | string + source?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + + +export type CheckInSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + habitId?: boolean + userId?: boolean + date?: boolean + seq?: boolean + note?: boolean + source?: boolean + createdAt?: boolean + habit?: boolean | Prisma.HabitDefaultArgs + user?: boolean | Prisma.UserDefaultArgs +}, ExtArgs["result"]["checkIn"]> + + + +export type CheckInSelectScalar = { + id?: boolean + habitId?: boolean + userId?: boolean + date?: boolean + seq?: boolean + note?: boolean + source?: boolean + createdAt?: boolean +} + +export type CheckInOmit = runtime.Types.Extensions.GetOmit<"id" | "habitId" | "userId" | "date" | "seq" | "note" | "source" | "createdAt", ExtArgs["result"]["checkIn"]> +export type CheckInInclude = { + habit?: boolean | Prisma.HabitDefaultArgs + user?: boolean | Prisma.UserDefaultArgs +} + +export type $CheckInPayload = { + name: "CheckIn" + objects: { + habit: Prisma.$HabitPayload + user: Prisma.$UserPayload + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number + habitId: number + userId: number + date: string + seq: number + note: string + source: string + createdAt: Date + }, ExtArgs["result"]["checkIn"]> + composites: {} +} + +export type CheckInGetPayload = runtime.Types.Result.GetResult + +export type CheckInCountArgs = + Omit & { + select?: CheckInCountAggregateInputType | true + } + +export interface CheckInDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CheckIn'], meta: { name: 'CheckIn' } } + /** + * Find zero or one CheckIn that matches the filter. + * @param {CheckInFindUniqueArgs} args - Arguments to find a CheckIn + * @example + * // Get one CheckIn + * const checkIn = await prisma.checkIn.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one CheckIn that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CheckInFindUniqueOrThrowArgs} args - Arguments to find a CheckIn + * @example + * // Get one CheckIn + * const checkIn = await prisma.checkIn.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckIn that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInFindFirstArgs} args - Arguments to find a CheckIn + * @example + * // Get one CheckIn + * const checkIn = await prisma.checkIn.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckIn that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInFindFirstOrThrowArgs} args - Arguments to find a CheckIn + * @example + * // Get one CheckIn + * const checkIn = await prisma.checkIn.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more CheckIns that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CheckIns + * const checkIns = await prisma.checkIn.findMany() + * + * // Get first 10 CheckIns + * const checkIns = await prisma.checkIn.findMany({ take: 10 }) + * + * // Only select the `id` + * const checkInWithIdOnly = await prisma.checkIn.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a CheckIn. + * @param {CheckInCreateArgs} args - Arguments to create a CheckIn. + * @example + * // Create one CheckIn + * const CheckIn = await prisma.checkIn.create({ + * data: { + * // ... data to create a CheckIn + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many CheckIns. + * @param {CheckInCreateManyArgs} args - Arguments to create many CheckIns. + * @example + * // Create many CheckIns + * const checkIn = await prisma.checkIn.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Delete a CheckIn. + * @param {CheckInDeleteArgs} args - Arguments to delete one CheckIn. + * @example + * // Delete one CheckIn + * const CheckIn = await prisma.checkIn.delete({ + * where: { + * // ... filter to delete one CheckIn + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one CheckIn. + * @param {CheckInUpdateArgs} args - Arguments to update one CheckIn. + * @example + * // Update one CheckIn + * const checkIn = await prisma.checkIn.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more CheckIns. + * @param {CheckInDeleteManyArgs} args - Arguments to filter CheckIns to delete. + * @example + * // Delete a few CheckIns + * const { count } = await prisma.checkIn.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CheckIns. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CheckIns + * const checkIn = await prisma.checkIn.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one CheckIn. + * @param {CheckInUpsertArgs} args - Arguments to update or create a CheckIn. + * @example + * // Update or create a CheckIn + * const checkIn = await prisma.checkIn.upsert({ + * create: { + * // ... data to create a CheckIn + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CheckIn we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__CheckInClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of CheckIns. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInCountArgs} args - Arguments to filter CheckIns to count. + * @example + * // Count the number of CheckIns + * const count = await prisma.checkIn.count({ + * where: { + * // ... the filter for the CheckIns we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CheckIn. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by CheckIn. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckInGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CheckInGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: CheckInGroupByArgs['orderBy'] } + : { orderBy?: CheckInGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetCheckInGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the CheckIn model + */ +readonly fields: CheckInFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for CheckIn. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__CheckInClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + habit = {}>(args?: Prisma.Subset>): Prisma.Prisma__HabitClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the CheckIn model + */ +export interface CheckInFieldRefs { + readonly id: Prisma.FieldRef<"CheckIn", 'Int'> + readonly habitId: Prisma.FieldRef<"CheckIn", 'Int'> + readonly userId: Prisma.FieldRef<"CheckIn", 'Int'> + readonly date: Prisma.FieldRef<"CheckIn", 'String'> + readonly seq: Prisma.FieldRef<"CheckIn", 'Int'> + readonly note: Prisma.FieldRef<"CheckIn", 'String'> + readonly source: Prisma.FieldRef<"CheckIn", 'String'> + readonly createdAt: Prisma.FieldRef<"CheckIn", 'DateTime'> +} + + +// Custom InputTypes +/** + * CheckIn findUnique + */ +export type CheckInFindUniqueArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter, which CheckIn to fetch. + */ + where: Prisma.CheckInWhereUniqueInput +} + +/** + * CheckIn findUniqueOrThrow + */ +export type CheckInFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter, which CheckIn to fetch. + */ + where: Prisma.CheckInWhereUniqueInput +} + +/** + * CheckIn findFirst + */ +export type CheckInFindFirstArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter, which CheckIn to fetch. + */ + where?: Prisma.CheckInWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckIns to fetch. + */ + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckIns. + */ + cursor?: Prisma.CheckInWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckIns from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckIns. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckIns. + */ + distinct?: Prisma.CheckInScalarFieldEnum | Prisma.CheckInScalarFieldEnum[] +} + +/** + * CheckIn findFirstOrThrow + */ +export type CheckInFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter, which CheckIn to fetch. + */ + where?: Prisma.CheckInWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckIns to fetch. + */ + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckIns. + */ + cursor?: Prisma.CheckInWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckIns from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckIns. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckIns. + */ + distinct?: Prisma.CheckInScalarFieldEnum | Prisma.CheckInScalarFieldEnum[] +} + +/** + * CheckIn findMany + */ +export type CheckInFindManyArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter, which CheckIns to fetch. + */ + where?: Prisma.CheckInWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckIns to fetch. + */ + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CheckIns. + */ + cursor?: Prisma.CheckInWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckIns from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckIns. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckIns. + */ + distinct?: Prisma.CheckInScalarFieldEnum | Prisma.CheckInScalarFieldEnum[] +} + +/** + * CheckIn create + */ +export type CheckInCreateArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * The data needed to create a CheckIn. + */ + data: Prisma.XOR +} + +/** + * CheckIn createMany + */ +export type CheckInCreateManyArgs = { + /** + * The data used to create many CheckIns. + */ + data: Prisma.CheckInCreateManyInput | Prisma.CheckInCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * CheckIn update + */ +export type CheckInUpdateArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * The data needed to update a CheckIn. + */ + data: Prisma.XOR + /** + * Choose, which CheckIn to update. + */ + where: Prisma.CheckInWhereUniqueInput +} + +/** + * CheckIn updateMany + */ +export type CheckInUpdateManyArgs = { + /** + * The data used to update CheckIns. + */ + data: Prisma.XOR + /** + * Filter which CheckIns to update + */ + where?: Prisma.CheckInWhereInput + /** + * Limit how many CheckIns to update. + */ + limit?: number +} + +/** + * CheckIn upsert + */ +export type CheckInUpsertArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * The filter to search for the CheckIn to update in case it exists. + */ + where: Prisma.CheckInWhereUniqueInput + /** + * In case the CheckIn found by the `where` argument doesn't exist, create a new CheckIn with this data. + */ + create: Prisma.XOR + /** + * In case the CheckIn was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * CheckIn delete + */ +export type CheckInDeleteArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + /** + * Filter which CheckIn to delete. + */ + where: Prisma.CheckInWhereUniqueInput +} + +/** + * CheckIn deleteMany + */ +export type CheckInDeleteManyArgs = { + /** + * Filter which CheckIns to delete + */ + where?: Prisma.CheckInWhereInput + /** + * Limit how many CheckIns to delete. + */ + limit?: number +} + +/** + * CheckIn without action + */ +export type CheckInDefaultArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null +} diff --git a/generated/prisma/models/CheckList.ts b/generated/prisma/models/CheckList.ts new file mode 100644 index 0000000..551ee73 --- /dev/null +++ b/generated/prisma/models/CheckList.ts @@ -0,0 +1,1514 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `CheckList` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model CheckList + * + */ +export type CheckListModel = runtime.Types.Result.DefaultSelection + +export type AggregateCheckList = { + _count: CheckListCountAggregateOutputType | null + _avg: CheckListAvgAggregateOutputType | null + _sum: CheckListSumAggregateOutputType | null + _min: CheckListMinAggregateOutputType | null + _max: CheckListMaxAggregateOutputType | null +} + +export type CheckListAvgAggregateOutputType = { + id: number | null + userId: number | null +} + +export type CheckListSumAggregateOutputType = { + id: number | null + userId: number | null +} + +export type CheckListMinAggregateOutputType = { + id: number | null + userId: number | null + title: string | null + scene: string | null + note: string | null + archived: boolean | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListMaxAggregateOutputType = { + id: number | null + userId: number | null + title: string | null + scene: string | null + note: string | null + archived: boolean | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListCountAggregateOutputType = { + id: number + userId: number + title: number + scene: number + note: number + archived: number + createdAt: number + updatedAt: number + _all: number +} + + +export type CheckListAvgAggregateInputType = { + id?: true + userId?: true +} + +export type CheckListSumAggregateInputType = { + id?: true + userId?: true +} + +export type CheckListMinAggregateInputType = { + id?: true + userId?: true + title?: true + scene?: true + note?: true + archived?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListMaxAggregateInputType = { + id?: true + userId?: true + title?: true + scene?: true + note?: true + archived?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListCountAggregateInputType = { + id?: true + userId?: true + title?: true + scene?: true + note?: true + archived?: true + createdAt?: true + updatedAt?: true + _all?: true +} + +export type CheckListAggregateArgs = { + /** + * Filter which CheckList to aggregate. + */ + where?: Prisma.CheckListWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckLists to fetch. + */ + orderBy?: Prisma.CheckListOrderByWithRelationInput | Prisma.CheckListOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.CheckListWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckLists from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckLists. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CheckLists + **/ + _count?: true | CheckListCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CheckListAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CheckListSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CheckListMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CheckListMaxAggregateInputType +} + +export type GetCheckListAggregateType = { + [P in keyof T & keyof AggregateCheckList]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type CheckListGroupByArgs = { + where?: Prisma.CheckListWhereInput + orderBy?: Prisma.CheckListOrderByWithAggregationInput | Prisma.CheckListOrderByWithAggregationInput[] + by: Prisma.CheckListScalarFieldEnum[] | Prisma.CheckListScalarFieldEnum + having?: Prisma.CheckListScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CheckListCountAggregateInputType | true + _avg?: CheckListAvgAggregateInputType + _sum?: CheckListSumAggregateInputType + _min?: CheckListMinAggregateInputType + _max?: CheckListMaxAggregateInputType +} + +export type CheckListGroupByOutputType = { + id: number + userId: number + title: string + scene: string + note: string + archived: boolean + createdAt: Date + updatedAt: Date + _count: CheckListCountAggregateOutputType | null + _avg: CheckListAvgAggregateOutputType | null + _sum: CheckListSumAggregateOutputType | null + _min: CheckListMinAggregateOutputType | null + _max: CheckListMaxAggregateOutputType | null +} + +export type GetCheckListGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof CheckListGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type CheckListWhereInput = { + AND?: Prisma.CheckListWhereInput | Prisma.CheckListWhereInput[] + OR?: Prisma.CheckListWhereInput[] + NOT?: Prisma.CheckListWhereInput | Prisma.CheckListWhereInput[] + id?: Prisma.IntFilter<"CheckList"> | number + userId?: Prisma.IntFilter<"CheckList"> | number + title?: Prisma.StringFilter<"CheckList"> | string + scene?: Prisma.StringFilter<"CheckList"> | string + note?: Prisma.StringFilter<"CheckList"> | string + archived?: Prisma.BoolFilter<"CheckList"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string + user?: Prisma.XOR + items?: Prisma.CheckListItemListRelationFilter +} + +export type CheckListOrderByWithRelationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + scene?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + user?: Prisma.UserOrderByWithRelationInput + items?: Prisma.CheckListItemOrderByRelationAggregateInput + _relevance?: Prisma.CheckListOrderByRelevanceInput +} + +export type CheckListWhereUniqueInput = Prisma.AtLeast<{ + id?: number + AND?: Prisma.CheckListWhereInput | Prisma.CheckListWhereInput[] + OR?: Prisma.CheckListWhereInput[] + NOT?: Prisma.CheckListWhereInput | Prisma.CheckListWhereInput[] + userId?: Prisma.IntFilter<"CheckList"> | number + title?: Prisma.StringFilter<"CheckList"> | string + scene?: Prisma.StringFilter<"CheckList"> | string + note?: Prisma.StringFilter<"CheckList"> | string + archived?: Prisma.BoolFilter<"CheckList"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string + user?: Prisma.XOR + items?: Prisma.CheckListItemListRelationFilter +}, "id"> + +export type CheckListOrderByWithAggregationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + scene?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + _count?: Prisma.CheckListCountOrderByAggregateInput + _avg?: Prisma.CheckListAvgOrderByAggregateInput + _max?: Prisma.CheckListMaxOrderByAggregateInput + _min?: Prisma.CheckListMinOrderByAggregateInput + _sum?: Prisma.CheckListSumOrderByAggregateInput +} + +export type CheckListScalarWhereWithAggregatesInput = { + AND?: Prisma.CheckListScalarWhereWithAggregatesInput | Prisma.CheckListScalarWhereWithAggregatesInput[] + OR?: Prisma.CheckListScalarWhereWithAggregatesInput[] + NOT?: Prisma.CheckListScalarWhereWithAggregatesInput | Prisma.CheckListScalarWhereWithAggregatesInput[] + id?: Prisma.IntWithAggregatesFilter<"CheckList"> | number + userId?: Prisma.IntWithAggregatesFilter<"CheckList"> | number + title?: Prisma.StringWithAggregatesFilter<"CheckList"> | string + scene?: Prisma.StringWithAggregatesFilter<"CheckList"> | string + note?: Prisma.StringWithAggregatesFilter<"CheckList"> | string + archived?: Prisma.BoolWithAggregatesFilter<"CheckList"> | boolean + createdAt?: Prisma.DateTimeWithAggregatesFilter<"CheckList"> | Date | string + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CheckList"> | Date | string +} + +export type CheckListCreateInput = { + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + user: Prisma.UserCreateNestedOneWithoutCheckListsInput + items?: Prisma.CheckListItemCreateNestedManyWithoutListInput +} + +export type CheckListUncheckedCreateInput = { + id?: number + userId: number + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + items?: Prisma.CheckListItemUncheckedCreateNestedManyWithoutListInput +} + +export type CheckListUpdateInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneRequiredWithoutCheckListsNestedInput + items?: Prisma.CheckListItemUpdateManyWithoutListNestedInput +} + +export type CheckListUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + items?: Prisma.CheckListItemUncheckedUpdateManyWithoutListNestedInput +} + +export type CheckListCreateManyInput = { + id?: number + userId: number + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListUpdateManyMutationInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListListRelationFilter = { + every?: Prisma.CheckListWhereInput + some?: Prisma.CheckListWhereInput + none?: Prisma.CheckListWhereInput +} + +export type CheckListOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type CheckListOrderByRelevanceInput = { + fields: Prisma.CheckListOrderByRelevanceFieldEnum | Prisma.CheckListOrderByRelevanceFieldEnum[] + sort: Prisma.SortOrder + search: string +} + +export type CheckListCountOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + scene?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListAvgOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder +} + +export type CheckListMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + scene?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListMinOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + scene?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListSumOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder +} + +export type CheckListScalarRelationFilter = { + is?: Prisma.CheckListWhereInput + isNot?: Prisma.CheckListWhereInput +} + +export type CheckListCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckListCreateWithoutUserInput[] | Prisma.CheckListUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutUserInput | Prisma.CheckListCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckListCreateManyUserInputEnvelope + connect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] +} + +export type CheckListUncheckedCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckListCreateWithoutUserInput[] | Prisma.CheckListUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutUserInput | Prisma.CheckListCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckListCreateManyUserInputEnvelope + connect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] +} + +export type CheckListUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckListCreateWithoutUserInput[] | Prisma.CheckListUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutUserInput | Prisma.CheckListCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckListUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckListUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckListCreateManyUserInputEnvelope + set?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + disconnect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + delete?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + connect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + update?: Prisma.CheckListUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckListUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckListUpdateManyWithWhereWithoutUserInput | Prisma.CheckListUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckListScalarWhereInput | Prisma.CheckListScalarWhereInput[] +} + +export type CheckListUncheckedUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckListCreateWithoutUserInput[] | Prisma.CheckListUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutUserInput | Prisma.CheckListCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckListUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckListUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckListCreateManyUserInputEnvelope + set?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + disconnect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + delete?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + connect?: Prisma.CheckListWhereUniqueInput | Prisma.CheckListWhereUniqueInput[] + update?: Prisma.CheckListUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckListUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckListUpdateManyWithWhereWithoutUserInput | Prisma.CheckListUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckListScalarWhereInput | Prisma.CheckListScalarWhereInput[] +} + +export type CheckListCreateNestedOneWithoutItemsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutItemsInput + connect?: Prisma.CheckListWhereUniqueInput +} + +export type CheckListUpdateOneRequiredWithoutItemsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CheckListCreateOrConnectWithoutItemsInput + upsert?: Prisma.CheckListUpsertWithoutItemsInput + connect?: Prisma.CheckListWhereUniqueInput + update?: Prisma.XOR, Prisma.CheckListUncheckedUpdateWithoutItemsInput> +} + +export type CheckListCreateWithoutUserInput = { + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + items?: Prisma.CheckListItemCreateNestedManyWithoutListInput +} + +export type CheckListUncheckedCreateWithoutUserInput = { + id?: number + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + items?: Prisma.CheckListItemUncheckedCreateNestedManyWithoutListInput +} + +export type CheckListCreateOrConnectWithoutUserInput = { + where: Prisma.CheckListWhereUniqueInput + create: Prisma.XOR +} + +export type CheckListCreateManyUserInputEnvelope = { + data: Prisma.CheckListCreateManyUserInput | Prisma.CheckListCreateManyUserInput[] + skipDuplicates?: boolean +} + +export type CheckListUpsertWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckListWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CheckListUpdateWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckListWhereUniqueInput + data: Prisma.XOR +} + +export type CheckListUpdateManyWithWhereWithoutUserInput = { + where: Prisma.CheckListScalarWhereInput + data: Prisma.XOR +} + +export type CheckListScalarWhereInput = { + AND?: Prisma.CheckListScalarWhereInput | Prisma.CheckListScalarWhereInput[] + OR?: Prisma.CheckListScalarWhereInput[] + NOT?: Prisma.CheckListScalarWhereInput | Prisma.CheckListScalarWhereInput[] + id?: Prisma.IntFilter<"CheckList"> | number + userId?: Prisma.IntFilter<"CheckList"> | number + title?: Prisma.StringFilter<"CheckList"> | string + scene?: Prisma.StringFilter<"CheckList"> | string + note?: Prisma.StringFilter<"CheckList"> | string + archived?: Prisma.BoolFilter<"CheckList"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckList"> | Date | string +} + +export type CheckListCreateWithoutItemsInput = { + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + user: Prisma.UserCreateNestedOneWithoutCheckListsInput +} + +export type CheckListUncheckedCreateWithoutItemsInput = { + id?: number + userId: number + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListCreateOrConnectWithoutItemsInput = { + where: Prisma.CheckListWhereUniqueInput + create: Prisma.XOR +} + +export type CheckListUpsertWithoutItemsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CheckListWhereInput +} + +export type CheckListUpdateToOneWithWhereWithoutItemsInput = { + where?: Prisma.CheckListWhereInput + data: Prisma.XOR +} + +export type CheckListUpdateWithoutItemsInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneRequiredWithoutCheckListsNestedInput +} + +export type CheckListUncheckedUpdateWithoutItemsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListCreateManyUserInput = { + id?: number + title: string + scene?: string + note?: string + archived?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListUpdateWithoutUserInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + items?: Prisma.CheckListItemUpdateManyWithoutListNestedInput +} + +export type CheckListUncheckedUpdateWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + items?: Prisma.CheckListItemUncheckedUpdateManyWithoutListNestedInput +} + +export type CheckListUncheckedUpdateManyWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + +/** + * Count Type CheckListCountOutputType + */ + +export type CheckListCountOutputType = { + items: number +} + +export type CheckListCountOutputTypeSelect = { + items?: boolean | CheckListCountOutputTypeCountItemsArgs +} + +/** + * CheckListCountOutputType without action + */ +export type CheckListCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the CheckListCountOutputType + */ + select?: Prisma.CheckListCountOutputTypeSelect | null +} + +/** + * CheckListCountOutputType without action + */ +export type CheckListCountOutputTypeCountItemsArgs = { + where?: Prisma.CheckListItemWhereInput +} + + +export type CheckListSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + title?: boolean + scene?: boolean + note?: boolean + archived?: boolean + createdAt?: boolean + updatedAt?: boolean + user?: boolean | Prisma.UserDefaultArgs + items?: boolean | Prisma.CheckList$itemsArgs + _count?: boolean | Prisma.CheckListCountOutputTypeDefaultArgs +}, ExtArgs["result"]["checkList"]> + + + +export type CheckListSelectScalar = { + id?: boolean + userId?: boolean + title?: boolean + scene?: boolean + note?: boolean + archived?: boolean + createdAt?: boolean + updatedAt?: boolean +} + +export type CheckListOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "title" | "scene" | "note" | "archived" | "createdAt" | "updatedAt", ExtArgs["result"]["checkList"]> +export type CheckListInclude = { + user?: boolean | Prisma.UserDefaultArgs + items?: boolean | Prisma.CheckList$itemsArgs + _count?: boolean | Prisma.CheckListCountOutputTypeDefaultArgs +} + +export type $CheckListPayload = { + name: "CheckList" + objects: { + user: Prisma.$UserPayload + items: Prisma.$CheckListItemPayload[] + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number + userId: number + title: string + scene: string + note: string + archived: boolean + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["checkList"]> + composites: {} +} + +export type CheckListGetPayload = runtime.Types.Result.GetResult + +export type CheckListCountArgs = + Omit & { + select?: CheckListCountAggregateInputType | true + } + +export interface CheckListDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CheckList'], meta: { name: 'CheckList' } } + /** + * Find zero or one CheckList that matches the filter. + * @param {CheckListFindUniqueArgs} args - Arguments to find a CheckList + * @example + * // Get one CheckList + * const checkList = await prisma.checkList.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one CheckList that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CheckListFindUniqueOrThrowArgs} args - Arguments to find a CheckList + * @example + * // Get one CheckList + * const checkList = await prisma.checkList.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckList that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListFindFirstArgs} args - Arguments to find a CheckList + * @example + * // Get one CheckList + * const checkList = await prisma.checkList.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckList that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListFindFirstOrThrowArgs} args - Arguments to find a CheckList + * @example + * // Get one CheckList + * const checkList = await prisma.checkList.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more CheckLists that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CheckLists + * const checkLists = await prisma.checkList.findMany() + * + * // Get first 10 CheckLists + * const checkLists = await prisma.checkList.findMany({ take: 10 }) + * + * // Only select the `id` + * const checkListWithIdOnly = await prisma.checkList.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a CheckList. + * @param {CheckListCreateArgs} args - Arguments to create a CheckList. + * @example + * // Create one CheckList + * const CheckList = await prisma.checkList.create({ + * data: { + * // ... data to create a CheckList + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many CheckLists. + * @param {CheckListCreateManyArgs} args - Arguments to create many CheckLists. + * @example + * // Create many CheckLists + * const checkList = await prisma.checkList.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Delete a CheckList. + * @param {CheckListDeleteArgs} args - Arguments to delete one CheckList. + * @example + * // Delete one CheckList + * const CheckList = await prisma.checkList.delete({ + * where: { + * // ... filter to delete one CheckList + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one CheckList. + * @param {CheckListUpdateArgs} args - Arguments to update one CheckList. + * @example + * // Update one CheckList + * const checkList = await prisma.checkList.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more CheckLists. + * @param {CheckListDeleteManyArgs} args - Arguments to filter CheckLists to delete. + * @example + * // Delete a few CheckLists + * const { count } = await prisma.checkList.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CheckLists. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CheckLists + * const checkList = await prisma.checkList.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one CheckList. + * @param {CheckListUpsertArgs} args - Arguments to update or create a CheckList. + * @example + * // Update or create a CheckList + * const checkList = await prisma.checkList.upsert({ + * create: { + * // ... data to create a CheckList + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CheckList we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of CheckLists. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListCountArgs} args - Arguments to filter CheckLists to count. + * @example + * // Count the number of CheckLists + * const count = await prisma.checkList.count({ + * where: { + * // ... the filter for the CheckLists we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CheckList. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by CheckList. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CheckListGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: CheckListGroupByArgs['orderBy'] } + : { orderBy?: CheckListGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetCheckListGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the CheckList model + */ +readonly fields: CheckListFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for CheckList. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__CheckListClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + items = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the CheckList model + */ +export interface CheckListFieldRefs { + readonly id: Prisma.FieldRef<"CheckList", 'Int'> + readonly userId: Prisma.FieldRef<"CheckList", 'Int'> + readonly title: Prisma.FieldRef<"CheckList", 'String'> + readonly scene: Prisma.FieldRef<"CheckList", 'String'> + readonly note: Prisma.FieldRef<"CheckList", 'String'> + readonly archived: Prisma.FieldRef<"CheckList", 'Boolean'> + readonly createdAt: Prisma.FieldRef<"CheckList", 'DateTime'> + readonly updatedAt: Prisma.FieldRef<"CheckList", 'DateTime'> +} + + +// Custom InputTypes +/** + * CheckList findUnique + */ +export type CheckListFindUniqueArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter, which CheckList to fetch. + */ + where: Prisma.CheckListWhereUniqueInput +} + +/** + * CheckList findUniqueOrThrow + */ +export type CheckListFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter, which CheckList to fetch. + */ + where: Prisma.CheckListWhereUniqueInput +} + +/** + * CheckList findFirst + */ +export type CheckListFindFirstArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter, which CheckList to fetch. + */ + where?: Prisma.CheckListWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckLists to fetch. + */ + orderBy?: Prisma.CheckListOrderByWithRelationInput | Prisma.CheckListOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckLists. + */ + cursor?: Prisma.CheckListWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckLists from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckLists. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckLists. + */ + distinct?: Prisma.CheckListScalarFieldEnum | Prisma.CheckListScalarFieldEnum[] +} + +/** + * CheckList findFirstOrThrow + */ +export type CheckListFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter, which CheckList to fetch. + */ + where?: Prisma.CheckListWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckLists to fetch. + */ + orderBy?: Prisma.CheckListOrderByWithRelationInput | Prisma.CheckListOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckLists. + */ + cursor?: Prisma.CheckListWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckLists from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckLists. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckLists. + */ + distinct?: Prisma.CheckListScalarFieldEnum | Prisma.CheckListScalarFieldEnum[] +} + +/** + * CheckList findMany + */ +export type CheckListFindManyArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter, which CheckLists to fetch. + */ + where?: Prisma.CheckListWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckLists to fetch. + */ + orderBy?: Prisma.CheckListOrderByWithRelationInput | Prisma.CheckListOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CheckLists. + */ + cursor?: Prisma.CheckListWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckLists from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckLists. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckLists. + */ + distinct?: Prisma.CheckListScalarFieldEnum | Prisma.CheckListScalarFieldEnum[] +} + +/** + * CheckList create + */ +export type CheckListCreateArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * The data needed to create a CheckList. + */ + data: Prisma.XOR +} + +/** + * CheckList createMany + */ +export type CheckListCreateManyArgs = { + /** + * The data used to create many CheckLists. + */ + data: Prisma.CheckListCreateManyInput | Prisma.CheckListCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * CheckList update + */ +export type CheckListUpdateArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * The data needed to update a CheckList. + */ + data: Prisma.XOR + /** + * Choose, which CheckList to update. + */ + where: Prisma.CheckListWhereUniqueInput +} + +/** + * CheckList updateMany + */ +export type CheckListUpdateManyArgs = { + /** + * The data used to update CheckLists. + */ + data: Prisma.XOR + /** + * Filter which CheckLists to update + */ + where?: Prisma.CheckListWhereInput + /** + * Limit how many CheckLists to update. + */ + limit?: number +} + +/** + * CheckList upsert + */ +export type CheckListUpsertArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * The filter to search for the CheckList to update in case it exists. + */ + where: Prisma.CheckListWhereUniqueInput + /** + * In case the CheckList found by the `where` argument doesn't exist, create a new CheckList with this data. + */ + create: Prisma.XOR + /** + * In case the CheckList was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * CheckList delete + */ +export type CheckListDeleteArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + /** + * Filter which CheckList to delete. + */ + where: Prisma.CheckListWhereUniqueInput +} + +/** + * CheckList deleteMany + */ +export type CheckListDeleteManyArgs = { + /** + * Filter which CheckLists to delete + */ + where?: Prisma.CheckListWhereInput + /** + * Limit how many CheckLists to delete. + */ + limit?: number +} + +/** + * CheckList.items + */ +export type CheckList$itemsArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + where?: Prisma.CheckListItemWhereInput + orderBy?: Prisma.CheckListItemOrderByWithRelationInput | Prisma.CheckListItemOrderByWithRelationInput[] + cursor?: Prisma.CheckListItemWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CheckListItemScalarFieldEnum | Prisma.CheckListItemScalarFieldEnum[] +} + +/** + * CheckList without action + */ +export type CheckListDefaultArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null +} diff --git a/generated/prisma/models/CheckListItem.ts b/generated/prisma/models/CheckListItem.ts new file mode 100644 index 0000000..df1e459 --- /dev/null +++ b/generated/prisma/models/CheckListItem.ts @@ -0,0 +1,1406 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `CheckListItem` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model CheckListItem + * + */ +export type CheckListItemModel = runtime.Types.Result.DefaultSelection + +export type AggregateCheckListItem = { + _count: CheckListItemCountAggregateOutputType | null + _avg: CheckListItemAvgAggregateOutputType | null + _sum: CheckListItemSumAggregateOutputType | null + _min: CheckListItemMinAggregateOutputType | null + _max: CheckListItemMaxAggregateOutputType | null +} + +export type CheckListItemAvgAggregateOutputType = { + id: number | null + listId: number | null + sortOrder: number | null +} + +export type CheckListItemSumAggregateOutputType = { + id: number | null + listId: number | null + sortOrder: number | null +} + +export type CheckListItemMinAggregateOutputType = { + id: number | null + listId: number | null + title: string | null + note: string | null + done: boolean | null + doneAt: Date | null + sortOrder: number | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListItemMaxAggregateOutputType = { + id: number | null + listId: number | null + title: string | null + note: string | null + done: boolean | null + doneAt: Date | null + sortOrder: number | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListItemCountAggregateOutputType = { + id: number + listId: number + title: number + note: number + done: number + doneAt: number + sortOrder: number + createdAt: number + updatedAt: number + _all: number +} + + +export type CheckListItemAvgAggregateInputType = { + id?: true + listId?: true + sortOrder?: true +} + +export type CheckListItemSumAggregateInputType = { + id?: true + listId?: true + sortOrder?: true +} + +export type CheckListItemMinAggregateInputType = { + id?: true + listId?: true + title?: true + note?: true + done?: true + doneAt?: true + sortOrder?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListItemMaxAggregateInputType = { + id?: true + listId?: true + title?: true + note?: true + done?: true + doneAt?: true + sortOrder?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListItemCountAggregateInputType = { + id?: true + listId?: true + title?: true + note?: true + done?: true + doneAt?: true + sortOrder?: true + createdAt?: true + updatedAt?: true + _all?: true +} + +export type CheckListItemAggregateArgs = { + /** + * Filter which CheckListItem to aggregate. + */ + where?: Prisma.CheckListItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListItems to fetch. + */ + orderBy?: Prisma.CheckListItemOrderByWithRelationInput | Prisma.CheckListItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.CheckListItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CheckListItems + **/ + _count?: true | CheckListItemCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CheckListItemAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CheckListItemSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CheckListItemMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CheckListItemMaxAggregateInputType +} + +export type GetCheckListItemAggregateType = { + [P in keyof T & keyof AggregateCheckListItem]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type CheckListItemGroupByArgs = { + where?: Prisma.CheckListItemWhereInput + orderBy?: Prisma.CheckListItemOrderByWithAggregationInput | Prisma.CheckListItemOrderByWithAggregationInput[] + by: Prisma.CheckListItemScalarFieldEnum[] | Prisma.CheckListItemScalarFieldEnum + having?: Prisma.CheckListItemScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CheckListItemCountAggregateInputType | true + _avg?: CheckListItemAvgAggregateInputType + _sum?: CheckListItemSumAggregateInputType + _min?: CheckListItemMinAggregateInputType + _max?: CheckListItemMaxAggregateInputType +} + +export type CheckListItemGroupByOutputType = { + id: number + listId: number + title: string + note: string + done: boolean + doneAt: Date | null + sortOrder: number + createdAt: Date + updatedAt: Date + _count: CheckListItemCountAggregateOutputType | null + _avg: CheckListItemAvgAggregateOutputType | null + _sum: CheckListItemSumAggregateOutputType | null + _min: CheckListItemMinAggregateOutputType | null + _max: CheckListItemMaxAggregateOutputType | null +} + +export type GetCheckListItemGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof CheckListItemGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type CheckListItemWhereInput = { + AND?: Prisma.CheckListItemWhereInput | Prisma.CheckListItemWhereInput[] + OR?: Prisma.CheckListItemWhereInput[] + NOT?: Prisma.CheckListItemWhereInput | Prisma.CheckListItemWhereInput[] + id?: Prisma.IntFilter<"CheckListItem"> | number + listId?: Prisma.IntFilter<"CheckListItem"> | number + title?: Prisma.StringFilter<"CheckListItem"> | string + note?: Prisma.StringFilter<"CheckListItem"> | string + done?: Prisma.BoolFilter<"CheckListItem"> | boolean + doneAt?: Prisma.DateTimeNullableFilter<"CheckListItem"> | Date | string | null + sortOrder?: Prisma.IntFilter<"CheckListItem"> | number + createdAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string + list?: Prisma.XOR +} + +export type CheckListItemOrderByWithRelationInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + title?: Prisma.SortOrder + note?: Prisma.SortOrder + done?: Prisma.SortOrder + doneAt?: Prisma.SortOrderInput | Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + list?: Prisma.CheckListOrderByWithRelationInput + _relevance?: Prisma.CheckListItemOrderByRelevanceInput +} + +export type CheckListItemWhereUniqueInput = Prisma.AtLeast<{ + id?: number + AND?: Prisma.CheckListItemWhereInput | Prisma.CheckListItemWhereInput[] + OR?: Prisma.CheckListItemWhereInput[] + NOT?: Prisma.CheckListItemWhereInput | Prisma.CheckListItemWhereInput[] + listId?: Prisma.IntFilter<"CheckListItem"> | number + title?: Prisma.StringFilter<"CheckListItem"> | string + note?: Prisma.StringFilter<"CheckListItem"> | string + done?: Prisma.BoolFilter<"CheckListItem"> | boolean + doneAt?: Prisma.DateTimeNullableFilter<"CheckListItem"> | Date | string | null + sortOrder?: Prisma.IntFilter<"CheckListItem"> | number + createdAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string + list?: Prisma.XOR +}, "id"> + +export type CheckListItemOrderByWithAggregationInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + title?: Prisma.SortOrder + note?: Prisma.SortOrder + done?: Prisma.SortOrder + doneAt?: Prisma.SortOrderInput | Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + _count?: Prisma.CheckListItemCountOrderByAggregateInput + _avg?: Prisma.CheckListItemAvgOrderByAggregateInput + _max?: Prisma.CheckListItemMaxOrderByAggregateInput + _min?: Prisma.CheckListItemMinOrderByAggregateInput + _sum?: Prisma.CheckListItemSumOrderByAggregateInput +} + +export type CheckListItemScalarWhereWithAggregatesInput = { + AND?: Prisma.CheckListItemScalarWhereWithAggregatesInput | Prisma.CheckListItemScalarWhereWithAggregatesInput[] + OR?: Prisma.CheckListItemScalarWhereWithAggregatesInput[] + NOT?: Prisma.CheckListItemScalarWhereWithAggregatesInput | Prisma.CheckListItemScalarWhereWithAggregatesInput[] + id?: Prisma.IntWithAggregatesFilter<"CheckListItem"> | number + listId?: Prisma.IntWithAggregatesFilter<"CheckListItem"> | number + title?: Prisma.StringWithAggregatesFilter<"CheckListItem"> | string + note?: Prisma.StringWithAggregatesFilter<"CheckListItem"> | string + done?: Prisma.BoolWithAggregatesFilter<"CheckListItem"> | boolean + doneAt?: Prisma.DateTimeNullableWithAggregatesFilter<"CheckListItem"> | Date | string | null + sortOrder?: Prisma.IntWithAggregatesFilter<"CheckListItem"> | number + createdAt?: Prisma.DateTimeWithAggregatesFilter<"CheckListItem"> | Date | string + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CheckListItem"> | Date | string +} + +export type CheckListItemCreateInput = { + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + list: Prisma.CheckListCreateNestedOneWithoutItemsInput +} + +export type CheckListItemUncheckedCreateInput = { + id?: number + listId: number + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListItemUpdateInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + list?: Prisma.CheckListUpdateOneRequiredWithoutItemsNestedInput +} + +export type CheckListItemUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + listId?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListItemCreateManyInput = { + id?: number + listId: number + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListItemUpdateManyMutationInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListItemUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + listId?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListItemListRelationFilter = { + every?: Prisma.CheckListItemWhereInput + some?: Prisma.CheckListItemWhereInput + none?: Prisma.CheckListItemWhereInput +} + +export type CheckListItemOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type CheckListItemOrderByRelevanceInput = { + fields: Prisma.CheckListItemOrderByRelevanceFieldEnum | Prisma.CheckListItemOrderByRelevanceFieldEnum[] + sort: Prisma.SortOrder + search: string +} + +export type CheckListItemCountOrderByAggregateInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + title?: Prisma.SortOrder + note?: Prisma.SortOrder + done?: Prisma.SortOrder + doneAt?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListItemAvgOrderByAggregateInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type CheckListItemMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + title?: Prisma.SortOrder + note?: Prisma.SortOrder + done?: Prisma.SortOrder + doneAt?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListItemMinOrderByAggregateInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + title?: Prisma.SortOrder + note?: Prisma.SortOrder + done?: Prisma.SortOrder + doneAt?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListItemSumOrderByAggregateInput = { + id?: Prisma.SortOrder + listId?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type CheckListItemCreateNestedManyWithoutListInput = { + create?: Prisma.XOR | Prisma.CheckListItemCreateWithoutListInput[] | Prisma.CheckListItemUncheckedCreateWithoutListInput[] + connectOrCreate?: Prisma.CheckListItemCreateOrConnectWithoutListInput | Prisma.CheckListItemCreateOrConnectWithoutListInput[] + createMany?: Prisma.CheckListItemCreateManyListInputEnvelope + connect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] +} + +export type CheckListItemUncheckedCreateNestedManyWithoutListInput = { + create?: Prisma.XOR | Prisma.CheckListItemCreateWithoutListInput[] | Prisma.CheckListItemUncheckedCreateWithoutListInput[] + connectOrCreate?: Prisma.CheckListItemCreateOrConnectWithoutListInput | Prisma.CheckListItemCreateOrConnectWithoutListInput[] + createMany?: Prisma.CheckListItemCreateManyListInputEnvelope + connect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] +} + +export type CheckListItemUpdateManyWithoutListNestedInput = { + create?: Prisma.XOR | Prisma.CheckListItemCreateWithoutListInput[] | Prisma.CheckListItemUncheckedCreateWithoutListInput[] + connectOrCreate?: Prisma.CheckListItemCreateOrConnectWithoutListInput | Prisma.CheckListItemCreateOrConnectWithoutListInput[] + upsert?: Prisma.CheckListItemUpsertWithWhereUniqueWithoutListInput | Prisma.CheckListItemUpsertWithWhereUniqueWithoutListInput[] + createMany?: Prisma.CheckListItemCreateManyListInputEnvelope + set?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + disconnect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + delete?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + connect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + update?: Prisma.CheckListItemUpdateWithWhereUniqueWithoutListInput | Prisma.CheckListItemUpdateWithWhereUniqueWithoutListInput[] + updateMany?: Prisma.CheckListItemUpdateManyWithWhereWithoutListInput | Prisma.CheckListItemUpdateManyWithWhereWithoutListInput[] + deleteMany?: Prisma.CheckListItemScalarWhereInput | Prisma.CheckListItemScalarWhereInput[] +} + +export type CheckListItemUncheckedUpdateManyWithoutListNestedInput = { + create?: Prisma.XOR | Prisma.CheckListItemCreateWithoutListInput[] | Prisma.CheckListItemUncheckedCreateWithoutListInput[] + connectOrCreate?: Prisma.CheckListItemCreateOrConnectWithoutListInput | Prisma.CheckListItemCreateOrConnectWithoutListInput[] + upsert?: Prisma.CheckListItemUpsertWithWhereUniqueWithoutListInput | Prisma.CheckListItemUpsertWithWhereUniqueWithoutListInput[] + createMany?: Prisma.CheckListItemCreateManyListInputEnvelope + set?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + disconnect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + delete?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + connect?: Prisma.CheckListItemWhereUniqueInput | Prisma.CheckListItemWhereUniqueInput[] + update?: Prisma.CheckListItemUpdateWithWhereUniqueWithoutListInput | Prisma.CheckListItemUpdateWithWhereUniqueWithoutListInput[] + updateMany?: Prisma.CheckListItemUpdateManyWithWhereWithoutListInput | Prisma.CheckListItemUpdateManyWithWhereWithoutListInput[] + deleteMany?: Prisma.CheckListItemScalarWhereInput | Prisma.CheckListItemScalarWhereInput[] +} + +export type CheckListItemCreateWithoutListInput = { + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListItemUncheckedCreateWithoutListInput = { + id?: number + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListItemCreateOrConnectWithoutListInput = { + where: Prisma.CheckListItemWhereUniqueInput + create: Prisma.XOR +} + +export type CheckListItemCreateManyListInputEnvelope = { + data: Prisma.CheckListItemCreateManyListInput | Prisma.CheckListItemCreateManyListInput[] + skipDuplicates?: boolean +} + +export type CheckListItemUpsertWithWhereUniqueWithoutListInput = { + where: Prisma.CheckListItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CheckListItemUpdateWithWhereUniqueWithoutListInput = { + where: Prisma.CheckListItemWhereUniqueInput + data: Prisma.XOR +} + +export type CheckListItemUpdateManyWithWhereWithoutListInput = { + where: Prisma.CheckListItemScalarWhereInput + data: Prisma.XOR +} + +export type CheckListItemScalarWhereInput = { + AND?: Prisma.CheckListItemScalarWhereInput | Prisma.CheckListItemScalarWhereInput[] + OR?: Prisma.CheckListItemScalarWhereInput[] + NOT?: Prisma.CheckListItemScalarWhereInput | Prisma.CheckListItemScalarWhereInput[] + id?: Prisma.IntFilter<"CheckListItem"> | number + listId?: Prisma.IntFilter<"CheckListItem"> | number + title?: Prisma.StringFilter<"CheckListItem"> | string + note?: Prisma.StringFilter<"CheckListItem"> | string + done?: Prisma.BoolFilter<"CheckListItem"> | boolean + doneAt?: Prisma.DateTimeNullableFilter<"CheckListItem"> | Date | string | null + sortOrder?: Prisma.IntFilter<"CheckListItem"> | number + createdAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListItem"> | Date | string +} + +export type CheckListItemCreateManyListInput = { + id?: number + title: string + note?: string + done?: boolean + doneAt?: Date | string | null + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListItemUpdateWithoutListInput = { + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListItemUncheckedUpdateWithoutListInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListItemUncheckedUpdateManyWithoutListInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + title?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + done?: Prisma.BoolFieldUpdateOperationsInput | boolean + doneAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + + +export type CheckListItemSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + listId?: boolean + title?: boolean + note?: boolean + done?: boolean + doneAt?: boolean + sortOrder?: boolean + createdAt?: boolean + updatedAt?: boolean + list?: boolean | Prisma.CheckListDefaultArgs +}, ExtArgs["result"]["checkListItem"]> + + + +export type CheckListItemSelectScalar = { + id?: boolean + listId?: boolean + title?: boolean + note?: boolean + done?: boolean + doneAt?: boolean + sortOrder?: boolean + createdAt?: boolean + updatedAt?: boolean +} + +export type CheckListItemOmit = runtime.Types.Extensions.GetOmit<"id" | "listId" | "title" | "note" | "done" | "doneAt" | "sortOrder" | "createdAt" | "updatedAt", ExtArgs["result"]["checkListItem"]> +export type CheckListItemInclude = { + list?: boolean | Prisma.CheckListDefaultArgs +} + +export type $CheckListItemPayload = { + name: "CheckListItem" + objects: { + list: Prisma.$CheckListPayload + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number + listId: number + title: string + note: string + done: boolean + doneAt: Date | null + sortOrder: number + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["checkListItem"]> + composites: {} +} + +export type CheckListItemGetPayload = runtime.Types.Result.GetResult + +export type CheckListItemCountArgs = + Omit & { + select?: CheckListItemCountAggregateInputType | true + } + +export interface CheckListItemDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CheckListItem'], meta: { name: 'CheckListItem' } } + /** + * Find zero or one CheckListItem that matches the filter. + * @param {CheckListItemFindUniqueArgs} args - Arguments to find a CheckListItem + * @example + * // Get one CheckListItem + * const checkListItem = await prisma.checkListItem.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one CheckListItem that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CheckListItemFindUniqueOrThrowArgs} args - Arguments to find a CheckListItem + * @example + * // Get one CheckListItem + * const checkListItem = await prisma.checkListItem.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckListItem that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemFindFirstArgs} args - Arguments to find a CheckListItem + * @example + * // Get one CheckListItem + * const checkListItem = await prisma.checkListItem.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckListItem that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemFindFirstOrThrowArgs} args - Arguments to find a CheckListItem + * @example + * // Get one CheckListItem + * const checkListItem = await prisma.checkListItem.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more CheckListItems that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CheckListItems + * const checkListItems = await prisma.checkListItem.findMany() + * + * // Get first 10 CheckListItems + * const checkListItems = await prisma.checkListItem.findMany({ take: 10 }) + * + * // Only select the `id` + * const checkListItemWithIdOnly = await prisma.checkListItem.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a CheckListItem. + * @param {CheckListItemCreateArgs} args - Arguments to create a CheckListItem. + * @example + * // Create one CheckListItem + * const CheckListItem = await prisma.checkListItem.create({ + * data: { + * // ... data to create a CheckListItem + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many CheckListItems. + * @param {CheckListItemCreateManyArgs} args - Arguments to create many CheckListItems. + * @example + * // Create many CheckListItems + * const checkListItem = await prisma.checkListItem.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Delete a CheckListItem. + * @param {CheckListItemDeleteArgs} args - Arguments to delete one CheckListItem. + * @example + * // Delete one CheckListItem + * const CheckListItem = await prisma.checkListItem.delete({ + * where: { + * // ... filter to delete one CheckListItem + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one CheckListItem. + * @param {CheckListItemUpdateArgs} args - Arguments to update one CheckListItem. + * @example + * // Update one CheckListItem + * const checkListItem = await prisma.checkListItem.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more CheckListItems. + * @param {CheckListItemDeleteManyArgs} args - Arguments to filter CheckListItems to delete. + * @example + * // Delete a few CheckListItems + * const { count } = await prisma.checkListItem.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CheckListItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CheckListItems + * const checkListItem = await prisma.checkListItem.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one CheckListItem. + * @param {CheckListItemUpsertArgs} args - Arguments to update or create a CheckListItem. + * @example + * // Update or create a CheckListItem + * const checkListItem = await prisma.checkListItem.upsert({ + * create: { + * // ... data to create a CheckListItem + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CheckListItem we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListItemClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of CheckListItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemCountArgs} args - Arguments to filter CheckListItems to count. + * @example + * // Count the number of CheckListItems + * const count = await prisma.checkListItem.count({ + * where: { + * // ... the filter for the CheckListItems we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CheckListItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by CheckListItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListItemGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CheckListItemGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: CheckListItemGroupByArgs['orderBy'] } + : { orderBy?: CheckListItemGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetCheckListItemGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the CheckListItem model + */ +readonly fields: CheckListItemFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for CheckListItem. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__CheckListItemClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + list = {}>(args?: Prisma.Subset>): Prisma.Prisma__CheckListClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the CheckListItem model + */ +export interface CheckListItemFieldRefs { + readonly id: Prisma.FieldRef<"CheckListItem", 'Int'> + readonly listId: Prisma.FieldRef<"CheckListItem", 'Int'> + readonly title: Prisma.FieldRef<"CheckListItem", 'String'> + readonly note: Prisma.FieldRef<"CheckListItem", 'String'> + readonly done: Prisma.FieldRef<"CheckListItem", 'Boolean'> + readonly doneAt: Prisma.FieldRef<"CheckListItem", 'DateTime'> + readonly sortOrder: Prisma.FieldRef<"CheckListItem", 'Int'> + readonly createdAt: Prisma.FieldRef<"CheckListItem", 'DateTime'> + readonly updatedAt: Prisma.FieldRef<"CheckListItem", 'DateTime'> +} + + +// Custom InputTypes +/** + * CheckListItem findUnique + */ +export type CheckListItemFindUniqueArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter, which CheckListItem to fetch. + */ + where: Prisma.CheckListItemWhereUniqueInput +} + +/** + * CheckListItem findUniqueOrThrow + */ +export type CheckListItemFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter, which CheckListItem to fetch. + */ + where: Prisma.CheckListItemWhereUniqueInput +} + +/** + * CheckListItem findFirst + */ +export type CheckListItemFindFirstArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter, which CheckListItem to fetch. + */ + where?: Prisma.CheckListItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListItems to fetch. + */ + orderBy?: Prisma.CheckListItemOrderByWithRelationInput | Prisma.CheckListItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckListItems. + */ + cursor?: Prisma.CheckListItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListItems. + */ + distinct?: Prisma.CheckListItemScalarFieldEnum | Prisma.CheckListItemScalarFieldEnum[] +} + +/** + * CheckListItem findFirstOrThrow + */ +export type CheckListItemFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter, which CheckListItem to fetch. + */ + where?: Prisma.CheckListItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListItems to fetch. + */ + orderBy?: Prisma.CheckListItemOrderByWithRelationInput | Prisma.CheckListItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckListItems. + */ + cursor?: Prisma.CheckListItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListItems. + */ + distinct?: Prisma.CheckListItemScalarFieldEnum | Prisma.CheckListItemScalarFieldEnum[] +} + +/** + * CheckListItem findMany + */ +export type CheckListItemFindManyArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter, which CheckListItems to fetch. + */ + where?: Prisma.CheckListItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListItems to fetch. + */ + orderBy?: Prisma.CheckListItemOrderByWithRelationInput | Prisma.CheckListItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CheckListItems. + */ + cursor?: Prisma.CheckListItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListItems. + */ + distinct?: Prisma.CheckListItemScalarFieldEnum | Prisma.CheckListItemScalarFieldEnum[] +} + +/** + * CheckListItem create + */ +export type CheckListItemCreateArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * The data needed to create a CheckListItem. + */ + data: Prisma.XOR +} + +/** + * CheckListItem createMany + */ +export type CheckListItemCreateManyArgs = { + /** + * The data used to create many CheckListItems. + */ + data: Prisma.CheckListItemCreateManyInput | Prisma.CheckListItemCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * CheckListItem update + */ +export type CheckListItemUpdateArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * The data needed to update a CheckListItem. + */ + data: Prisma.XOR + /** + * Choose, which CheckListItem to update. + */ + where: Prisma.CheckListItemWhereUniqueInput +} + +/** + * CheckListItem updateMany + */ +export type CheckListItemUpdateManyArgs = { + /** + * The data used to update CheckListItems. + */ + data: Prisma.XOR + /** + * Filter which CheckListItems to update + */ + where?: Prisma.CheckListItemWhereInput + /** + * Limit how many CheckListItems to update. + */ + limit?: number +} + +/** + * CheckListItem upsert + */ +export type CheckListItemUpsertArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * The filter to search for the CheckListItem to update in case it exists. + */ + where: Prisma.CheckListItemWhereUniqueInput + /** + * In case the CheckListItem found by the `where` argument doesn't exist, create a new CheckListItem with this data. + */ + create: Prisma.XOR + /** + * In case the CheckListItem was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * CheckListItem delete + */ +export type CheckListItemDeleteArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null + /** + * Filter which CheckListItem to delete. + */ + where: Prisma.CheckListItemWhereUniqueInput +} + +/** + * CheckListItem deleteMany + */ +export type CheckListItemDeleteManyArgs = { + /** + * Filter which CheckListItems to delete + */ + where?: Prisma.CheckListItemWhereInput + /** + * Limit how many CheckListItems to delete. + */ + limit?: number +} + +/** + * CheckListItem without action + */ +export type CheckListItemDefaultArgs = { + /** + * Select specific fields to fetch from the CheckListItem + */ + select?: Prisma.CheckListItemSelect | null + /** + * Omit specific fields from the CheckListItem + */ + omit?: Prisma.CheckListItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListItemInclude | null +} diff --git a/generated/prisma/models/CheckListTemplate.ts b/generated/prisma/models/CheckListTemplate.ts new file mode 100644 index 0000000..5f9debe --- /dev/null +++ b/generated/prisma/models/CheckListTemplate.ts @@ -0,0 +1,1380 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `CheckListTemplate` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model CheckListTemplate + * + */ +export type CheckListTemplateModel = runtime.Types.Result.DefaultSelection + +export type AggregateCheckListTemplate = { + _count: CheckListTemplateCountAggregateOutputType | null + _avg: CheckListTemplateAvgAggregateOutputType | null + _sum: CheckListTemplateSumAggregateOutputType | null + _min: CheckListTemplateMinAggregateOutputType | null + _max: CheckListTemplateMaxAggregateOutputType | null +} + +export type CheckListTemplateAvgAggregateOutputType = { + id: number | null + userId: number | null +} + +export type CheckListTemplateSumAggregateOutputType = { + id: number | null + userId: number | null +} + +export type CheckListTemplateMinAggregateOutputType = { + id: number | null + userId: number | null + name: string | null + scene: string | null + builtin: boolean | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListTemplateMaxAggregateOutputType = { + id: number | null + userId: number | null + name: string | null + scene: string | null + builtin: boolean | null + createdAt: Date | null + updatedAt: Date | null +} + +export type CheckListTemplateCountAggregateOutputType = { + id: number + userId: number + name: number + scene: number + items: number + builtin: number + createdAt: number + updatedAt: number + _all: number +} + + +export type CheckListTemplateAvgAggregateInputType = { + id?: true + userId?: true +} + +export type CheckListTemplateSumAggregateInputType = { + id?: true + userId?: true +} + +export type CheckListTemplateMinAggregateInputType = { + id?: true + userId?: true + name?: true + scene?: true + builtin?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListTemplateMaxAggregateInputType = { + id?: true + userId?: true + name?: true + scene?: true + builtin?: true + createdAt?: true + updatedAt?: true +} + +export type CheckListTemplateCountAggregateInputType = { + id?: true + userId?: true + name?: true + scene?: true + items?: true + builtin?: true + createdAt?: true + updatedAt?: true + _all?: true +} + +export type CheckListTemplateAggregateArgs = { + /** + * Filter which CheckListTemplate to aggregate. + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListTemplates to fetch. + */ + orderBy?: Prisma.CheckListTemplateOrderByWithRelationInput | Prisma.CheckListTemplateOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.CheckListTemplateWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListTemplates from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListTemplates. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CheckListTemplates + **/ + _count?: true | CheckListTemplateCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CheckListTemplateAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CheckListTemplateSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CheckListTemplateMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CheckListTemplateMaxAggregateInputType +} + +export type GetCheckListTemplateAggregateType = { + [P in keyof T & keyof AggregateCheckListTemplate]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type CheckListTemplateGroupByArgs = { + where?: Prisma.CheckListTemplateWhereInput + orderBy?: Prisma.CheckListTemplateOrderByWithAggregationInput | Prisma.CheckListTemplateOrderByWithAggregationInput[] + by: Prisma.CheckListTemplateScalarFieldEnum[] | Prisma.CheckListTemplateScalarFieldEnum + having?: Prisma.CheckListTemplateScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CheckListTemplateCountAggregateInputType | true + _avg?: CheckListTemplateAvgAggregateInputType + _sum?: CheckListTemplateSumAggregateInputType + _min?: CheckListTemplateMinAggregateInputType + _max?: CheckListTemplateMaxAggregateInputType +} + +export type CheckListTemplateGroupByOutputType = { + id: number + userId: number | null + name: string + scene: string + items: runtime.JsonValue + builtin: boolean + createdAt: Date + updatedAt: Date + _count: CheckListTemplateCountAggregateOutputType | null + _avg: CheckListTemplateAvgAggregateOutputType | null + _sum: CheckListTemplateSumAggregateOutputType | null + _min: CheckListTemplateMinAggregateOutputType | null + _max: CheckListTemplateMaxAggregateOutputType | null +} + +export type GetCheckListTemplateGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof CheckListTemplateGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type CheckListTemplateWhereInput = { + AND?: Prisma.CheckListTemplateWhereInput | Prisma.CheckListTemplateWhereInput[] + OR?: Prisma.CheckListTemplateWhereInput[] + NOT?: Prisma.CheckListTemplateWhereInput | Prisma.CheckListTemplateWhereInput[] + id?: Prisma.IntFilter<"CheckListTemplate"> | number + userId?: Prisma.IntNullableFilter<"CheckListTemplate"> | number | null + name?: Prisma.StringFilter<"CheckListTemplate"> | string + scene?: Prisma.StringFilter<"CheckListTemplate"> | string + items?: Prisma.JsonFilter<"CheckListTemplate"> + builtin?: Prisma.BoolFilter<"CheckListTemplate"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string + user?: Prisma.XOR | null +} + +export type CheckListTemplateOrderByWithRelationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrderInput | Prisma.SortOrder + name?: Prisma.SortOrder + scene?: Prisma.SortOrder + items?: Prisma.SortOrder + builtin?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + user?: Prisma.UserOrderByWithRelationInput + _relevance?: Prisma.CheckListTemplateOrderByRelevanceInput +} + +export type CheckListTemplateWhereUniqueInput = Prisma.AtLeast<{ + id?: number + AND?: Prisma.CheckListTemplateWhereInput | Prisma.CheckListTemplateWhereInput[] + OR?: Prisma.CheckListTemplateWhereInput[] + NOT?: Prisma.CheckListTemplateWhereInput | Prisma.CheckListTemplateWhereInput[] + userId?: Prisma.IntNullableFilter<"CheckListTemplate"> | number | null + name?: Prisma.StringFilter<"CheckListTemplate"> | string + scene?: Prisma.StringFilter<"CheckListTemplate"> | string + items?: Prisma.JsonFilter<"CheckListTemplate"> + builtin?: Prisma.BoolFilter<"CheckListTemplate"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string + user?: Prisma.XOR | null +}, "id"> + +export type CheckListTemplateOrderByWithAggregationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrderInput | Prisma.SortOrder + name?: Prisma.SortOrder + scene?: Prisma.SortOrder + items?: Prisma.SortOrder + builtin?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + _count?: Prisma.CheckListTemplateCountOrderByAggregateInput + _avg?: Prisma.CheckListTemplateAvgOrderByAggregateInput + _max?: Prisma.CheckListTemplateMaxOrderByAggregateInput + _min?: Prisma.CheckListTemplateMinOrderByAggregateInput + _sum?: Prisma.CheckListTemplateSumOrderByAggregateInput +} + +export type CheckListTemplateScalarWhereWithAggregatesInput = { + AND?: Prisma.CheckListTemplateScalarWhereWithAggregatesInput | Prisma.CheckListTemplateScalarWhereWithAggregatesInput[] + OR?: Prisma.CheckListTemplateScalarWhereWithAggregatesInput[] + NOT?: Prisma.CheckListTemplateScalarWhereWithAggregatesInput | Prisma.CheckListTemplateScalarWhereWithAggregatesInput[] + id?: Prisma.IntWithAggregatesFilter<"CheckListTemplate"> | number + userId?: Prisma.IntNullableWithAggregatesFilter<"CheckListTemplate"> | number | null + name?: Prisma.StringWithAggregatesFilter<"CheckListTemplate"> | string + scene?: Prisma.StringWithAggregatesFilter<"CheckListTemplate"> | string + items?: Prisma.JsonWithAggregatesFilter<"CheckListTemplate"> + builtin?: Prisma.BoolWithAggregatesFilter<"CheckListTemplate"> | boolean + createdAt?: Prisma.DateTimeWithAggregatesFilter<"CheckListTemplate"> | Date | string + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CheckListTemplate"> | Date | string +} + +export type CheckListTemplateCreateInput = { + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string + user?: Prisma.UserCreateNestedOneWithoutTemplatesInput +} + +export type CheckListTemplateUncheckedCreateInput = { + id?: number + userId?: number | null + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListTemplateUpdateInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneWithoutTemplatesNestedInput +} + +export type CheckListTemplateUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListTemplateCreateManyInput = { + id?: number + userId?: number | null + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListTemplateUpdateManyMutationInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListTemplateUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListTemplateListRelationFilter = { + every?: Prisma.CheckListTemplateWhereInput + some?: Prisma.CheckListTemplateWhereInput + none?: Prisma.CheckListTemplateWhereInput +} + +export type CheckListTemplateOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type CheckListTemplateOrderByRelevanceInput = { + fields: Prisma.CheckListTemplateOrderByRelevanceFieldEnum | Prisma.CheckListTemplateOrderByRelevanceFieldEnum[] + sort: Prisma.SortOrder + search: string +} + +export type CheckListTemplateCountOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + scene?: Prisma.SortOrder + items?: Prisma.SortOrder + builtin?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListTemplateAvgOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder +} + +export type CheckListTemplateMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + scene?: Prisma.SortOrder + builtin?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListTemplateMinOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + scene?: Prisma.SortOrder + builtin?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type CheckListTemplateSumOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder +} + +export type CheckListTemplateCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckListTemplateCreateWithoutUserInput[] | Prisma.CheckListTemplateUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListTemplateCreateOrConnectWithoutUserInput | Prisma.CheckListTemplateCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckListTemplateCreateManyUserInputEnvelope + connect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] +} + +export type CheckListTemplateUncheckedCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.CheckListTemplateCreateWithoutUserInput[] | Prisma.CheckListTemplateUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListTemplateCreateOrConnectWithoutUserInput | Prisma.CheckListTemplateCreateOrConnectWithoutUserInput[] + createMany?: Prisma.CheckListTemplateCreateManyUserInputEnvelope + connect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] +} + +export type CheckListTemplateUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckListTemplateCreateWithoutUserInput[] | Prisma.CheckListTemplateUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListTemplateCreateOrConnectWithoutUserInput | Prisma.CheckListTemplateCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckListTemplateUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckListTemplateUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckListTemplateCreateManyUserInputEnvelope + set?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + disconnect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + delete?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + connect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + update?: Prisma.CheckListTemplateUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckListTemplateUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckListTemplateUpdateManyWithWhereWithoutUserInput | Prisma.CheckListTemplateUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckListTemplateScalarWhereInput | Prisma.CheckListTemplateScalarWhereInput[] +} + +export type CheckListTemplateUncheckedUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.CheckListTemplateCreateWithoutUserInput[] | Prisma.CheckListTemplateUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.CheckListTemplateCreateOrConnectWithoutUserInput | Prisma.CheckListTemplateCreateOrConnectWithoutUserInput[] + upsert?: Prisma.CheckListTemplateUpsertWithWhereUniqueWithoutUserInput | Prisma.CheckListTemplateUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.CheckListTemplateCreateManyUserInputEnvelope + set?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + disconnect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + delete?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + connect?: Prisma.CheckListTemplateWhereUniqueInput | Prisma.CheckListTemplateWhereUniqueInput[] + update?: Prisma.CheckListTemplateUpdateWithWhereUniqueWithoutUserInput | Prisma.CheckListTemplateUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.CheckListTemplateUpdateManyWithWhereWithoutUserInput | Prisma.CheckListTemplateUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.CheckListTemplateScalarWhereInput | Prisma.CheckListTemplateScalarWhereInput[] +} + +export type CheckListTemplateCreateWithoutUserInput = { + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListTemplateUncheckedCreateWithoutUserInput = { + id?: number + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListTemplateCreateOrConnectWithoutUserInput = { + where: Prisma.CheckListTemplateWhereUniqueInput + create: Prisma.XOR +} + +export type CheckListTemplateCreateManyUserInputEnvelope = { + data: Prisma.CheckListTemplateCreateManyUserInput | Prisma.CheckListTemplateCreateManyUserInput[] + skipDuplicates?: boolean +} + +export type CheckListTemplateUpsertWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckListTemplateWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CheckListTemplateUpdateWithWhereUniqueWithoutUserInput = { + where: Prisma.CheckListTemplateWhereUniqueInput + data: Prisma.XOR +} + +export type CheckListTemplateUpdateManyWithWhereWithoutUserInput = { + where: Prisma.CheckListTemplateScalarWhereInput + data: Prisma.XOR +} + +export type CheckListTemplateScalarWhereInput = { + AND?: Prisma.CheckListTemplateScalarWhereInput | Prisma.CheckListTemplateScalarWhereInput[] + OR?: Prisma.CheckListTemplateScalarWhereInput[] + NOT?: Prisma.CheckListTemplateScalarWhereInput | Prisma.CheckListTemplateScalarWhereInput[] + id?: Prisma.IntFilter<"CheckListTemplate"> | number + userId?: Prisma.IntNullableFilter<"CheckListTemplate"> | number | null + name?: Prisma.StringFilter<"CheckListTemplate"> | string + scene?: Prisma.StringFilter<"CheckListTemplate"> | string + items?: Prisma.JsonFilter<"CheckListTemplate"> + builtin?: Prisma.BoolFilter<"CheckListTemplate"> | boolean + createdAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CheckListTemplate"> | Date | string +} + +export type CheckListTemplateCreateManyUserInput = { + id?: number + name: string + scene?: string + items: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: boolean + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CheckListTemplateUpdateWithoutUserInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListTemplateUncheckedUpdateWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CheckListTemplateUncheckedUpdateManyWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + scene?: Prisma.StringFieldUpdateOperationsInput | string + items?: Prisma.JsonNullValueInput | runtime.InputJsonValue + builtin?: Prisma.BoolFieldUpdateOperationsInput | boolean + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + + +export type CheckListTemplateSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + name?: boolean + scene?: boolean + items?: boolean + builtin?: boolean + createdAt?: boolean + updatedAt?: boolean + user?: boolean | Prisma.CheckListTemplate$userArgs +}, ExtArgs["result"]["checkListTemplate"]> + + + +export type CheckListTemplateSelectScalar = { + id?: boolean + userId?: boolean + name?: boolean + scene?: boolean + items?: boolean + builtin?: boolean + createdAt?: boolean + updatedAt?: boolean +} + +export type CheckListTemplateOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "name" | "scene" | "items" | "builtin" | "createdAt" | "updatedAt", ExtArgs["result"]["checkListTemplate"]> +export type CheckListTemplateInclude = { + user?: boolean | Prisma.CheckListTemplate$userArgs +} + +export type $CheckListTemplatePayload = { + name: "CheckListTemplate" + objects: { + user: Prisma.$UserPayload | null + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number + userId: number | null + name: string + scene: string + items: runtime.JsonValue + builtin: boolean + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["checkListTemplate"]> + composites: {} +} + +export type CheckListTemplateGetPayload = runtime.Types.Result.GetResult + +export type CheckListTemplateCountArgs = + Omit & { + select?: CheckListTemplateCountAggregateInputType | true + } + +export interface CheckListTemplateDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CheckListTemplate'], meta: { name: 'CheckListTemplate' } } + /** + * Find zero or one CheckListTemplate that matches the filter. + * @param {CheckListTemplateFindUniqueArgs} args - Arguments to find a CheckListTemplate + * @example + * // Get one CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one CheckListTemplate that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CheckListTemplateFindUniqueOrThrowArgs} args - Arguments to find a CheckListTemplate + * @example + * // Get one CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckListTemplate that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateFindFirstArgs} args - Arguments to find a CheckListTemplate + * @example + * // Get one CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CheckListTemplate that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateFindFirstOrThrowArgs} args - Arguments to find a CheckListTemplate + * @example + * // Get one CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more CheckListTemplates that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CheckListTemplates + * const checkListTemplates = await prisma.checkListTemplate.findMany() + * + * // Get first 10 CheckListTemplates + * const checkListTemplates = await prisma.checkListTemplate.findMany({ take: 10 }) + * + * // Only select the `id` + * const checkListTemplateWithIdOnly = await prisma.checkListTemplate.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a CheckListTemplate. + * @param {CheckListTemplateCreateArgs} args - Arguments to create a CheckListTemplate. + * @example + * // Create one CheckListTemplate + * const CheckListTemplate = await prisma.checkListTemplate.create({ + * data: { + * // ... data to create a CheckListTemplate + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many CheckListTemplates. + * @param {CheckListTemplateCreateManyArgs} args - Arguments to create many CheckListTemplates. + * @example + * // Create many CheckListTemplates + * const checkListTemplate = await prisma.checkListTemplate.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Delete a CheckListTemplate. + * @param {CheckListTemplateDeleteArgs} args - Arguments to delete one CheckListTemplate. + * @example + * // Delete one CheckListTemplate + * const CheckListTemplate = await prisma.checkListTemplate.delete({ + * where: { + * // ... filter to delete one CheckListTemplate + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one CheckListTemplate. + * @param {CheckListTemplateUpdateArgs} args - Arguments to update one CheckListTemplate. + * @example + * // Update one CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more CheckListTemplates. + * @param {CheckListTemplateDeleteManyArgs} args - Arguments to filter CheckListTemplates to delete. + * @example + * // Delete a few CheckListTemplates + * const { count } = await prisma.checkListTemplate.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CheckListTemplates. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CheckListTemplates + * const checkListTemplate = await prisma.checkListTemplate.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one CheckListTemplate. + * @param {CheckListTemplateUpsertArgs} args - Arguments to update or create a CheckListTemplate. + * @example + * // Update or create a CheckListTemplate + * const checkListTemplate = await prisma.checkListTemplate.upsert({ + * create: { + * // ... data to create a CheckListTemplate + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CheckListTemplate we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__CheckListTemplateClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of CheckListTemplates. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateCountArgs} args - Arguments to filter CheckListTemplates to count. + * @example + * // Count the number of CheckListTemplates + * const count = await prisma.checkListTemplate.count({ + * where: { + * // ... the filter for the CheckListTemplates we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CheckListTemplate. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by CheckListTemplate. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CheckListTemplateGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CheckListTemplateGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: CheckListTemplateGroupByArgs['orderBy'] } + : { orderBy?: CheckListTemplateGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetCheckListTemplateGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the CheckListTemplate model + */ +readonly fields: CheckListTemplateFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for CheckListTemplate. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__CheckListTemplateClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the CheckListTemplate model + */ +export interface CheckListTemplateFieldRefs { + readonly id: Prisma.FieldRef<"CheckListTemplate", 'Int'> + readonly userId: Prisma.FieldRef<"CheckListTemplate", 'Int'> + readonly name: Prisma.FieldRef<"CheckListTemplate", 'String'> + readonly scene: Prisma.FieldRef<"CheckListTemplate", 'String'> + readonly items: Prisma.FieldRef<"CheckListTemplate", 'Json'> + readonly builtin: Prisma.FieldRef<"CheckListTemplate", 'Boolean'> + readonly createdAt: Prisma.FieldRef<"CheckListTemplate", 'DateTime'> + readonly updatedAt: Prisma.FieldRef<"CheckListTemplate", 'DateTime'> +} + + +// Custom InputTypes +/** + * CheckListTemplate findUnique + */ +export type CheckListTemplateFindUniqueArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter, which CheckListTemplate to fetch. + */ + where: Prisma.CheckListTemplateWhereUniqueInput +} + +/** + * CheckListTemplate findUniqueOrThrow + */ +export type CheckListTemplateFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter, which CheckListTemplate to fetch. + */ + where: Prisma.CheckListTemplateWhereUniqueInput +} + +/** + * CheckListTemplate findFirst + */ +export type CheckListTemplateFindFirstArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter, which CheckListTemplate to fetch. + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListTemplates to fetch. + */ + orderBy?: Prisma.CheckListTemplateOrderByWithRelationInput | Prisma.CheckListTemplateOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckListTemplates. + */ + cursor?: Prisma.CheckListTemplateWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListTemplates from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListTemplates. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListTemplates. + */ + distinct?: Prisma.CheckListTemplateScalarFieldEnum | Prisma.CheckListTemplateScalarFieldEnum[] +} + +/** + * CheckListTemplate findFirstOrThrow + */ +export type CheckListTemplateFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter, which CheckListTemplate to fetch. + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListTemplates to fetch. + */ + orderBy?: Prisma.CheckListTemplateOrderByWithRelationInput | Prisma.CheckListTemplateOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CheckListTemplates. + */ + cursor?: Prisma.CheckListTemplateWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListTemplates from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListTemplates. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListTemplates. + */ + distinct?: Prisma.CheckListTemplateScalarFieldEnum | Prisma.CheckListTemplateScalarFieldEnum[] +} + +/** + * CheckListTemplate findMany + */ +export type CheckListTemplateFindManyArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter, which CheckListTemplates to fetch. + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CheckListTemplates to fetch. + */ + orderBy?: Prisma.CheckListTemplateOrderByWithRelationInput | Prisma.CheckListTemplateOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CheckListTemplates. + */ + cursor?: Prisma.CheckListTemplateWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CheckListTemplates from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CheckListTemplates. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CheckListTemplates. + */ + distinct?: Prisma.CheckListTemplateScalarFieldEnum | Prisma.CheckListTemplateScalarFieldEnum[] +} + +/** + * CheckListTemplate create + */ +export type CheckListTemplateCreateArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * The data needed to create a CheckListTemplate. + */ + data: Prisma.XOR +} + +/** + * CheckListTemplate createMany + */ +export type CheckListTemplateCreateManyArgs = { + /** + * The data used to create many CheckListTemplates. + */ + data: Prisma.CheckListTemplateCreateManyInput | Prisma.CheckListTemplateCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * CheckListTemplate update + */ +export type CheckListTemplateUpdateArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * The data needed to update a CheckListTemplate. + */ + data: Prisma.XOR + /** + * Choose, which CheckListTemplate to update. + */ + where: Prisma.CheckListTemplateWhereUniqueInput +} + +/** + * CheckListTemplate updateMany + */ +export type CheckListTemplateUpdateManyArgs = { + /** + * The data used to update CheckListTemplates. + */ + data: Prisma.XOR + /** + * Filter which CheckListTemplates to update + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * Limit how many CheckListTemplates to update. + */ + limit?: number +} + +/** + * CheckListTemplate upsert + */ +export type CheckListTemplateUpsertArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * The filter to search for the CheckListTemplate to update in case it exists. + */ + where: Prisma.CheckListTemplateWhereUniqueInput + /** + * In case the CheckListTemplate found by the `where` argument doesn't exist, create a new CheckListTemplate with this data. + */ + create: Prisma.XOR + /** + * In case the CheckListTemplate was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * CheckListTemplate delete + */ +export type CheckListTemplateDeleteArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + /** + * Filter which CheckListTemplate to delete. + */ + where: Prisma.CheckListTemplateWhereUniqueInput +} + +/** + * CheckListTemplate deleteMany + */ +export type CheckListTemplateDeleteManyArgs = { + /** + * Filter which CheckListTemplates to delete + */ + where?: Prisma.CheckListTemplateWhereInput + /** + * Limit how many CheckListTemplates to delete. + */ + limit?: number +} + +/** + * CheckListTemplate.user + */ +export type CheckListTemplate$userArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + +/** + * CheckListTemplate without action + */ +export type CheckListTemplateDefaultArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null +} diff --git a/generated/prisma/models/Habit.ts b/generated/prisma/models/Habit.ts new file mode 100644 index 0000000..22795ad --- /dev/null +++ b/generated/prisma/models/Habit.ts @@ -0,0 +1,1674 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `Habit` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model Habit + * + */ +export type HabitModel = runtime.Types.Result.DefaultSelection + +export type AggregateHabit = { + _count: HabitCountAggregateOutputType | null + _avg: HabitAvgAggregateOutputType | null + _sum: HabitSumAggregateOutputType | null + _min: HabitMinAggregateOutputType | null + _max: HabitMaxAggregateOutputType | null +} + +export type HabitAvgAggregateOutputType = { + id: number | null + userId: number | null + target: number | null + sortOrder: number | null +} + +export type HabitSumAggregateOutputType = { + id: number | null + userId: number | null + target: number | null + sortOrder: number | null +} + +export type HabitMinAggregateOutputType = { + id: number | null + userId: number | null + name: string | null + period: string | null + target: number | null + color: string | null + icon: string | null + note: string | null + archived: boolean | null + sortOrder: number | null + createdAt: Date | null + updatedAt: Date | null +} + +export type HabitMaxAggregateOutputType = { + id: number | null + userId: number | null + name: string | null + period: string | null + target: number | null + color: string | null + icon: string | null + note: string | null + archived: boolean | null + sortOrder: number | null + createdAt: Date | null + updatedAt: Date | null +} + +export type HabitCountAggregateOutputType = { + id: number + userId: number + name: number + period: number + target: number + color: number + icon: number + note: number + archived: number + sortOrder: number + createdAt: number + updatedAt: number + _all: number +} + + +export type HabitAvgAggregateInputType = { + id?: true + userId?: true + target?: true + sortOrder?: true +} + +export type HabitSumAggregateInputType = { + id?: true + userId?: true + target?: true + sortOrder?: true +} + +export type HabitMinAggregateInputType = { + id?: true + userId?: true + name?: true + period?: true + target?: true + color?: true + icon?: true + note?: true + archived?: true + sortOrder?: true + createdAt?: true + updatedAt?: true +} + +export type HabitMaxAggregateInputType = { + id?: true + userId?: true + name?: true + period?: true + target?: true + color?: true + icon?: true + note?: true + archived?: true + sortOrder?: true + createdAt?: true + updatedAt?: true +} + +export type HabitCountAggregateInputType = { + id?: true + userId?: true + name?: true + period?: true + target?: true + color?: true + icon?: true + note?: true + archived?: true + sortOrder?: true + createdAt?: true + updatedAt?: true + _all?: true +} + +export type HabitAggregateArgs = { + /** + * Filter which Habit to aggregate. + */ + where?: Prisma.HabitWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Habits to fetch. + */ + orderBy?: Prisma.HabitOrderByWithRelationInput | Prisma.HabitOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.HabitWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Habits from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Habits. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned Habits + **/ + _count?: true | HabitCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: HabitAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: HabitSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: HabitMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: HabitMaxAggregateInputType +} + +export type GetHabitAggregateType = { + [P in keyof T & keyof AggregateHabit]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type HabitGroupByArgs = { + where?: Prisma.HabitWhereInput + orderBy?: Prisma.HabitOrderByWithAggregationInput | Prisma.HabitOrderByWithAggregationInput[] + by: Prisma.HabitScalarFieldEnum[] | Prisma.HabitScalarFieldEnum + having?: Prisma.HabitScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: HabitCountAggregateInputType | true + _avg?: HabitAvgAggregateInputType + _sum?: HabitSumAggregateInputType + _min?: HabitMinAggregateInputType + _max?: HabitMaxAggregateInputType +} + +export type HabitGroupByOutputType = { + id: number + userId: number + name: string + period: string + target: number + color: string + icon: string + note: string + archived: boolean + sortOrder: number + createdAt: Date + updatedAt: Date + _count: HabitCountAggregateOutputType | null + _avg: HabitAvgAggregateOutputType | null + _sum: HabitSumAggregateOutputType | null + _min: HabitMinAggregateOutputType | null + _max: HabitMaxAggregateOutputType | null +} + +export type GetHabitGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof HabitGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type HabitWhereInput = { + AND?: Prisma.HabitWhereInput | Prisma.HabitWhereInput[] + OR?: Prisma.HabitWhereInput[] + NOT?: Prisma.HabitWhereInput | Prisma.HabitWhereInput[] + id?: Prisma.IntFilter<"Habit"> | number + userId?: Prisma.IntFilter<"Habit"> | number + name?: Prisma.StringFilter<"Habit"> | string + period?: Prisma.StringFilter<"Habit"> | string + target?: Prisma.IntFilter<"Habit"> | number + color?: Prisma.StringFilter<"Habit"> | string + icon?: Prisma.StringFilter<"Habit"> | string + note?: Prisma.StringFilter<"Habit"> | string + archived?: Prisma.BoolFilter<"Habit"> | boolean + sortOrder?: Prisma.IntFilter<"Habit"> | number + createdAt?: Prisma.DateTimeFilter<"Habit"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Habit"> | Date | string + user?: Prisma.XOR + records?: Prisma.CheckInListRelationFilter +} + +export type HabitOrderByWithRelationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + period?: Prisma.SortOrder + target?: Prisma.SortOrder + color?: Prisma.SortOrder + icon?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + user?: Prisma.UserOrderByWithRelationInput + records?: Prisma.CheckInOrderByRelationAggregateInput + _relevance?: Prisma.HabitOrderByRelevanceInput +} + +export type HabitWhereUniqueInput = Prisma.AtLeast<{ + id?: number + AND?: Prisma.HabitWhereInput | Prisma.HabitWhereInput[] + OR?: Prisma.HabitWhereInput[] + NOT?: Prisma.HabitWhereInput | Prisma.HabitWhereInput[] + userId?: Prisma.IntFilter<"Habit"> | number + name?: Prisma.StringFilter<"Habit"> | string + period?: Prisma.StringFilter<"Habit"> | string + target?: Prisma.IntFilter<"Habit"> | number + color?: Prisma.StringFilter<"Habit"> | string + icon?: Prisma.StringFilter<"Habit"> | string + note?: Prisma.StringFilter<"Habit"> | string + archived?: Prisma.BoolFilter<"Habit"> | boolean + sortOrder?: Prisma.IntFilter<"Habit"> | number + createdAt?: Prisma.DateTimeFilter<"Habit"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Habit"> | Date | string + user?: Prisma.XOR + records?: Prisma.CheckInListRelationFilter +}, "id"> + +export type HabitOrderByWithAggregationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + period?: Prisma.SortOrder + target?: Prisma.SortOrder + color?: Prisma.SortOrder + icon?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + _count?: Prisma.HabitCountOrderByAggregateInput + _avg?: Prisma.HabitAvgOrderByAggregateInput + _max?: Prisma.HabitMaxOrderByAggregateInput + _min?: Prisma.HabitMinOrderByAggregateInput + _sum?: Prisma.HabitSumOrderByAggregateInput +} + +export type HabitScalarWhereWithAggregatesInput = { + AND?: Prisma.HabitScalarWhereWithAggregatesInput | Prisma.HabitScalarWhereWithAggregatesInput[] + OR?: Prisma.HabitScalarWhereWithAggregatesInput[] + NOT?: Prisma.HabitScalarWhereWithAggregatesInput | Prisma.HabitScalarWhereWithAggregatesInput[] + id?: Prisma.IntWithAggregatesFilter<"Habit"> | number + userId?: Prisma.IntWithAggregatesFilter<"Habit"> | number + name?: Prisma.StringWithAggregatesFilter<"Habit"> | string + period?: Prisma.StringWithAggregatesFilter<"Habit"> | string + target?: Prisma.IntWithAggregatesFilter<"Habit"> | number + color?: Prisma.StringWithAggregatesFilter<"Habit"> | string + icon?: Prisma.StringWithAggregatesFilter<"Habit"> | string + note?: Prisma.StringWithAggregatesFilter<"Habit"> | string + archived?: Prisma.BoolWithAggregatesFilter<"Habit"> | boolean + sortOrder?: Prisma.IntWithAggregatesFilter<"Habit"> | number + createdAt?: Prisma.DateTimeWithAggregatesFilter<"Habit"> | Date | string + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Habit"> | Date | string +} + +export type HabitCreateInput = { + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + user: Prisma.UserCreateNestedOneWithoutHabitsInput + records?: Prisma.CheckInCreateNestedManyWithoutHabitInput +} + +export type HabitUncheckedCreateInput = { + id?: number + userId: number + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + records?: Prisma.CheckInUncheckedCreateNestedManyWithoutHabitInput +} + +export type HabitUpdateInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneRequiredWithoutHabitsNestedInput + records?: Prisma.CheckInUpdateManyWithoutHabitNestedInput +} + +export type HabitUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + records?: Prisma.CheckInUncheckedUpdateManyWithoutHabitNestedInput +} + +export type HabitCreateManyInput = { + id?: number + userId: number + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type HabitUpdateManyMutationInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type HabitUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type HabitListRelationFilter = { + every?: Prisma.HabitWhereInput + some?: Prisma.HabitWhereInput + none?: Prisma.HabitWhereInput +} + +export type HabitOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type HabitOrderByRelevanceInput = { + fields: Prisma.HabitOrderByRelevanceFieldEnum | Prisma.HabitOrderByRelevanceFieldEnum[] + sort: Prisma.SortOrder + search: string +} + +export type HabitCountOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + period?: Prisma.SortOrder + target?: Prisma.SortOrder + color?: Prisma.SortOrder + icon?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type HabitAvgOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + target?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type HabitMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + period?: Prisma.SortOrder + target?: Prisma.SortOrder + color?: Prisma.SortOrder + icon?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type HabitMinOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + name?: Prisma.SortOrder + period?: Prisma.SortOrder + target?: Prisma.SortOrder + color?: Prisma.SortOrder + icon?: Prisma.SortOrder + note?: Prisma.SortOrder + archived?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type HabitSumOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + target?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type HabitScalarRelationFilter = { + is?: Prisma.HabitWhereInput + isNot?: Prisma.HabitWhereInput +} + +export type HabitCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.HabitCreateWithoutUserInput[] | Prisma.HabitUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutUserInput | Prisma.HabitCreateOrConnectWithoutUserInput[] + createMany?: Prisma.HabitCreateManyUserInputEnvelope + connect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] +} + +export type HabitUncheckedCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.HabitCreateWithoutUserInput[] | Prisma.HabitUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutUserInput | Prisma.HabitCreateOrConnectWithoutUserInput[] + createMany?: Prisma.HabitCreateManyUserInputEnvelope + connect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] +} + +export type HabitUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.HabitCreateWithoutUserInput[] | Prisma.HabitUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutUserInput | Prisma.HabitCreateOrConnectWithoutUserInput[] + upsert?: Prisma.HabitUpsertWithWhereUniqueWithoutUserInput | Prisma.HabitUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.HabitCreateManyUserInputEnvelope + set?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + disconnect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + delete?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + connect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + update?: Prisma.HabitUpdateWithWhereUniqueWithoutUserInput | Prisma.HabitUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.HabitUpdateManyWithWhereWithoutUserInput | Prisma.HabitUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.HabitScalarWhereInput | Prisma.HabitScalarWhereInput[] +} + +export type HabitUncheckedUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.HabitCreateWithoutUserInput[] | Prisma.HabitUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutUserInput | Prisma.HabitCreateOrConnectWithoutUserInput[] + upsert?: Prisma.HabitUpsertWithWhereUniqueWithoutUserInput | Prisma.HabitUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.HabitCreateManyUserInputEnvelope + set?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + disconnect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + delete?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + connect?: Prisma.HabitWhereUniqueInput | Prisma.HabitWhereUniqueInput[] + update?: Prisma.HabitUpdateWithWhereUniqueWithoutUserInput | Prisma.HabitUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.HabitUpdateManyWithWhereWithoutUserInput | Prisma.HabitUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.HabitScalarWhereInput | Prisma.HabitScalarWhereInput[] +} + +export type HabitCreateNestedOneWithoutRecordsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutRecordsInput + connect?: Prisma.HabitWhereUniqueInput +} + +export type HabitUpdateOneRequiredWithoutRecordsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.HabitCreateOrConnectWithoutRecordsInput + upsert?: Prisma.HabitUpsertWithoutRecordsInput + connect?: Prisma.HabitWhereUniqueInput + update?: Prisma.XOR, Prisma.HabitUncheckedUpdateWithoutRecordsInput> +} + +export type HabitCreateWithoutUserInput = { + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + records?: Prisma.CheckInCreateNestedManyWithoutHabitInput +} + +export type HabitUncheckedCreateWithoutUserInput = { + id?: number + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + records?: Prisma.CheckInUncheckedCreateNestedManyWithoutHabitInput +} + +export type HabitCreateOrConnectWithoutUserInput = { + where: Prisma.HabitWhereUniqueInput + create: Prisma.XOR +} + +export type HabitCreateManyUserInputEnvelope = { + data: Prisma.HabitCreateManyUserInput | Prisma.HabitCreateManyUserInput[] + skipDuplicates?: boolean +} + +export type HabitUpsertWithWhereUniqueWithoutUserInput = { + where: Prisma.HabitWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type HabitUpdateWithWhereUniqueWithoutUserInput = { + where: Prisma.HabitWhereUniqueInput + data: Prisma.XOR +} + +export type HabitUpdateManyWithWhereWithoutUserInput = { + where: Prisma.HabitScalarWhereInput + data: Prisma.XOR +} + +export type HabitScalarWhereInput = { + AND?: Prisma.HabitScalarWhereInput | Prisma.HabitScalarWhereInput[] + OR?: Prisma.HabitScalarWhereInput[] + NOT?: Prisma.HabitScalarWhereInput | Prisma.HabitScalarWhereInput[] + id?: Prisma.IntFilter<"Habit"> | number + userId?: Prisma.IntFilter<"Habit"> | number + name?: Prisma.StringFilter<"Habit"> | string + period?: Prisma.StringFilter<"Habit"> | string + target?: Prisma.IntFilter<"Habit"> | number + color?: Prisma.StringFilter<"Habit"> | string + icon?: Prisma.StringFilter<"Habit"> | string + note?: Prisma.StringFilter<"Habit"> | string + archived?: Prisma.BoolFilter<"Habit"> | boolean + sortOrder?: Prisma.IntFilter<"Habit"> | number + createdAt?: Prisma.DateTimeFilter<"Habit"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Habit"> | Date | string +} + +export type HabitCreateWithoutRecordsInput = { + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string + user: Prisma.UserCreateNestedOneWithoutHabitsInput +} + +export type HabitUncheckedCreateWithoutRecordsInput = { + id?: number + userId: number + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type HabitCreateOrConnectWithoutRecordsInput = { + where: Prisma.HabitWhereUniqueInput + create: Prisma.XOR +} + +export type HabitUpsertWithoutRecordsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.HabitWhereInput +} + +export type HabitUpdateToOneWithWhereWithoutRecordsInput = { + where?: Prisma.HabitWhereInput + data: Prisma.XOR +} + +export type HabitUpdateWithoutRecordsInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneRequiredWithoutHabitsNestedInput +} + +export type HabitUncheckedUpdateWithoutRecordsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + userId?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type HabitCreateManyUserInput = { + id?: number + name: string + period?: string + target?: number + color?: string + icon?: string + note?: string + archived?: boolean + sortOrder?: number + createdAt?: Date | string + updatedAt?: Date | string +} + +export type HabitUpdateWithoutUserInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + records?: Prisma.CheckInUpdateManyWithoutHabitNestedInput +} + +export type HabitUncheckedUpdateWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + records?: Prisma.CheckInUncheckedUpdateManyWithoutHabitNestedInput +} + +export type HabitUncheckedUpdateManyWithoutUserInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + period?: Prisma.StringFieldUpdateOperationsInput | string + target?: Prisma.IntFieldUpdateOperationsInput | number + color?: Prisma.StringFieldUpdateOperationsInput | string + icon?: Prisma.StringFieldUpdateOperationsInput | string + note?: Prisma.StringFieldUpdateOperationsInput | string + archived?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + +/** + * Count Type HabitCountOutputType + */ + +export type HabitCountOutputType = { + records: number +} + +export type HabitCountOutputTypeSelect = { + records?: boolean | HabitCountOutputTypeCountRecordsArgs +} + +/** + * HabitCountOutputType without action + */ +export type HabitCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the HabitCountOutputType + */ + select?: Prisma.HabitCountOutputTypeSelect | null +} + +/** + * HabitCountOutputType without action + */ +export type HabitCountOutputTypeCountRecordsArgs = { + where?: Prisma.CheckInWhereInput +} + + +export type HabitSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + name?: boolean + period?: boolean + target?: boolean + color?: boolean + icon?: boolean + note?: boolean + archived?: boolean + sortOrder?: boolean + createdAt?: boolean + updatedAt?: boolean + user?: boolean | Prisma.UserDefaultArgs + records?: boolean | Prisma.Habit$recordsArgs + _count?: boolean | Prisma.HabitCountOutputTypeDefaultArgs +}, ExtArgs["result"]["habit"]> + + + +export type HabitSelectScalar = { + id?: boolean + userId?: boolean + name?: boolean + period?: boolean + target?: boolean + color?: boolean + icon?: boolean + note?: boolean + archived?: boolean + sortOrder?: boolean + createdAt?: boolean + updatedAt?: boolean +} + +export type HabitOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "name" | "period" | "target" | "color" | "icon" | "note" | "archived" | "sortOrder" | "createdAt" | "updatedAt", ExtArgs["result"]["habit"]> +export type HabitInclude = { + user?: boolean | Prisma.UserDefaultArgs + records?: boolean | Prisma.Habit$recordsArgs + _count?: boolean | Prisma.HabitCountOutputTypeDefaultArgs +} + +export type $HabitPayload = { + name: "Habit" + objects: { + user: Prisma.$UserPayload + records: Prisma.$CheckInPayload[] + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number + userId: number + name: string + period: string + target: number + color: string + icon: string + note: string + archived: boolean + sortOrder: number + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["habit"]> + composites: {} +} + +export type HabitGetPayload = runtime.Types.Result.GetResult + +export type HabitCountArgs = + Omit & { + select?: HabitCountAggregateInputType | true + } + +export interface HabitDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['Habit'], meta: { name: 'Habit' } } + /** + * Find zero or one Habit that matches the filter. + * @param {HabitFindUniqueArgs} args - Arguments to find a Habit + * @example + * // Get one Habit + * const habit = await prisma.habit.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one Habit that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {HabitFindUniqueOrThrowArgs} args - Arguments to find a Habit + * @example + * // Get one Habit + * const habit = await prisma.habit.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Habit that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitFindFirstArgs} args - Arguments to find a Habit + * @example + * // Get one Habit + * const habit = await prisma.habit.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Habit that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitFindFirstOrThrowArgs} args - Arguments to find a Habit + * @example + * // Get one Habit + * const habit = await prisma.habit.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more Habits that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all Habits + * const habits = await prisma.habit.findMany() + * + * // Get first 10 Habits + * const habits = await prisma.habit.findMany({ take: 10 }) + * + * // Only select the `id` + * const habitWithIdOnly = await prisma.habit.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a Habit. + * @param {HabitCreateArgs} args - Arguments to create a Habit. + * @example + * // Create one Habit + * const Habit = await prisma.habit.create({ + * data: { + * // ... data to create a Habit + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many Habits. + * @param {HabitCreateManyArgs} args - Arguments to create many Habits. + * @example + * // Create many Habits + * const habit = await prisma.habit.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Delete a Habit. + * @param {HabitDeleteArgs} args - Arguments to delete one Habit. + * @example + * // Delete one Habit + * const Habit = await prisma.habit.delete({ + * where: { + * // ... filter to delete one Habit + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one Habit. + * @param {HabitUpdateArgs} args - Arguments to update one Habit. + * @example + * // Update one Habit + * const habit = await prisma.habit.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more Habits. + * @param {HabitDeleteManyArgs} args - Arguments to filter Habits to delete. + * @example + * // Delete a few Habits + * const { count } = await prisma.habit.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more Habits. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many Habits + * const habit = await prisma.habit.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one Habit. + * @param {HabitUpsertArgs} args - Arguments to update or create a Habit. + * @example + * // Update or create a Habit + * const habit = await prisma.habit.upsert({ + * create: { + * // ... data to create a Habit + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the Habit we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__HabitClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of Habits. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitCountArgs} args - Arguments to filter Habits to count. + * @example + * // Count the number of Habits + * const count = await prisma.habit.count({ + * where: { + * // ... the filter for the Habits we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a Habit. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by Habit. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {HabitGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends HabitGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: HabitGroupByArgs['orderBy'] } + : { orderBy?: HabitGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetHabitGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the Habit model + */ +readonly fields: HabitFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for Habit. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__HabitClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + records = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the Habit model + */ +export interface HabitFieldRefs { + readonly id: Prisma.FieldRef<"Habit", 'Int'> + readonly userId: Prisma.FieldRef<"Habit", 'Int'> + readonly name: Prisma.FieldRef<"Habit", 'String'> + readonly period: Prisma.FieldRef<"Habit", 'String'> + readonly target: Prisma.FieldRef<"Habit", 'Int'> + readonly color: Prisma.FieldRef<"Habit", 'String'> + readonly icon: Prisma.FieldRef<"Habit", 'String'> + readonly note: Prisma.FieldRef<"Habit", 'String'> + readonly archived: Prisma.FieldRef<"Habit", 'Boolean'> + readonly sortOrder: Prisma.FieldRef<"Habit", 'Int'> + readonly createdAt: Prisma.FieldRef<"Habit", 'DateTime'> + readonly updatedAt: Prisma.FieldRef<"Habit", 'DateTime'> +} + + +// Custom InputTypes +/** + * Habit findUnique + */ +export type HabitFindUniqueArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter, which Habit to fetch. + */ + where: Prisma.HabitWhereUniqueInput +} + +/** + * Habit findUniqueOrThrow + */ +export type HabitFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter, which Habit to fetch. + */ + where: Prisma.HabitWhereUniqueInput +} + +/** + * Habit findFirst + */ +export type HabitFindFirstArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter, which Habit to fetch. + */ + where?: Prisma.HabitWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Habits to fetch. + */ + orderBy?: Prisma.HabitOrderByWithRelationInput | Prisma.HabitOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Habits. + */ + cursor?: Prisma.HabitWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Habits from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Habits. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Habits. + */ + distinct?: Prisma.HabitScalarFieldEnum | Prisma.HabitScalarFieldEnum[] +} + +/** + * Habit findFirstOrThrow + */ +export type HabitFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter, which Habit to fetch. + */ + where?: Prisma.HabitWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Habits to fetch. + */ + orderBy?: Prisma.HabitOrderByWithRelationInput | Prisma.HabitOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Habits. + */ + cursor?: Prisma.HabitWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Habits from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Habits. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Habits. + */ + distinct?: Prisma.HabitScalarFieldEnum | Prisma.HabitScalarFieldEnum[] +} + +/** + * Habit findMany + */ +export type HabitFindManyArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter, which Habits to fetch. + */ + where?: Prisma.HabitWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Habits to fetch. + */ + orderBy?: Prisma.HabitOrderByWithRelationInput | Prisma.HabitOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing Habits. + */ + cursor?: Prisma.HabitWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Habits from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Habits. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Habits. + */ + distinct?: Prisma.HabitScalarFieldEnum | Prisma.HabitScalarFieldEnum[] +} + +/** + * Habit create + */ +export type HabitCreateArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * The data needed to create a Habit. + */ + data: Prisma.XOR +} + +/** + * Habit createMany + */ +export type HabitCreateManyArgs = { + /** + * The data used to create many Habits. + */ + data: Prisma.HabitCreateManyInput | Prisma.HabitCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * Habit update + */ +export type HabitUpdateArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * The data needed to update a Habit. + */ + data: Prisma.XOR + /** + * Choose, which Habit to update. + */ + where: Prisma.HabitWhereUniqueInput +} + +/** + * Habit updateMany + */ +export type HabitUpdateManyArgs = { + /** + * The data used to update Habits. + */ + data: Prisma.XOR + /** + * Filter which Habits to update + */ + where?: Prisma.HabitWhereInput + /** + * Limit how many Habits to update. + */ + limit?: number +} + +/** + * Habit upsert + */ +export type HabitUpsertArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * The filter to search for the Habit to update in case it exists. + */ + where: Prisma.HabitWhereUniqueInput + /** + * In case the Habit found by the `where` argument doesn't exist, create a new Habit with this data. + */ + create: Prisma.XOR + /** + * In case the Habit was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * Habit delete + */ +export type HabitDeleteArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + /** + * Filter which Habit to delete. + */ + where: Prisma.HabitWhereUniqueInput +} + +/** + * Habit deleteMany + */ +export type HabitDeleteManyArgs = { + /** + * Filter which Habits to delete + */ + where?: Prisma.HabitWhereInput + /** + * Limit how many Habits to delete. + */ + limit?: number +} + +/** + * Habit.records + */ +export type Habit$recordsArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + where?: Prisma.CheckInWhereInput + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + cursor?: Prisma.CheckInWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CheckInScalarFieldEnum | Prisma.CheckInScalarFieldEnum[] +} + +/** + * Habit without action + */ +export type HabitDefaultArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null +} diff --git a/generated/prisma/models/User.ts b/generated/prisma/models/User.ts index de9031e..3f166ca 100644 --- a/generated/prisma/models/User.ts +++ b/generated/prisma/models/User.ts @@ -224,6 +224,10 @@ export type UserWhereInput = { password?: Prisma.StringFilter<"User"> | string createdAt?: Prisma.DateTimeFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string + habits?: Prisma.HabitListRelationFilter + checkIns?: Prisma.CheckInListRelationFilter + checkLists?: Prisma.CheckListListRelationFilter + templates?: Prisma.CheckListTemplateListRelationFilter } export type UserOrderByWithRelationInput = { @@ -233,6 +237,10 @@ export type UserOrderByWithRelationInput = { password?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder + habits?: Prisma.HabitOrderByRelationAggregateInput + checkIns?: Prisma.CheckInOrderByRelationAggregateInput + checkLists?: Prisma.CheckListOrderByRelationAggregateInput + templates?: Prisma.CheckListTemplateOrderByRelationAggregateInput _relevance?: Prisma.UserOrderByRelevanceInput } @@ -246,6 +254,10 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{ password?: Prisma.StringFilter<"User"> | string createdAt?: Prisma.DateTimeFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string + habits?: Prisma.HabitListRelationFilter + checkIns?: Prisma.CheckInListRelationFilter + checkLists?: Prisma.CheckListListRelationFilter + templates?: Prisma.CheckListTemplateListRelationFilter }, "id" | "email"> export type UserOrderByWithAggregationInput = { @@ -280,6 +292,10 @@ export type UserCreateInput = { password: string createdAt?: Date | string updatedAt?: Date | string + habits?: Prisma.HabitCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateCreateNestedManyWithoutUserInput } export type UserUncheckedCreateInput = { @@ -289,6 +305,10 @@ export type UserUncheckedCreateInput = { password: string createdAt?: Date | string updatedAt?: Date | string + habits?: Prisma.HabitUncheckedCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInUncheckedCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListUncheckedCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateUncheckedCreateNestedManyWithoutUserInput } export type UserUpdateInput = { @@ -297,6 +317,10 @@ export type UserUpdateInput = { password?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUpdateManyWithoutUserNestedInput } export type UserUncheckedUpdateInput = { @@ -306,6 +330,10 @@ export type UserUncheckedUpdateInput = { password?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUncheckedUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUncheckedUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUncheckedUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUncheckedUpdateManyWithoutUserNestedInput } export type UserCreateManyInput = { @@ -375,6 +403,16 @@ export type UserSumOrderByAggregateInput = { id?: Prisma.SortOrder } +export type UserScalarRelationFilter = { + is?: Prisma.UserWhereInput + isNot?: Prisma.UserWhereInput +} + +export type UserNullableScalarRelationFilter = { + is?: Prisma.UserWhereInput | null + isNot?: Prisma.UserWhereInput | null +} + export type StringFieldUpdateOperationsInput = { set?: string } @@ -391,6 +429,368 @@ export type IntFieldUpdateOperationsInput = { divide?: number } +export type UserCreateNestedOneWithoutHabitsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutHabitsInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneRequiredWithoutHabitsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutHabitsInput + upsert?: Prisma.UserUpsertWithoutHabitsInput + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutHabitsInput> +} + +export type UserCreateNestedOneWithoutCheckInsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCheckInsInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneRequiredWithoutCheckInsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCheckInsInput + upsert?: Prisma.UserUpsertWithoutCheckInsInput + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCheckInsInput> +} + +export type UserCreateNestedOneWithoutCheckListsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCheckListsInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneRequiredWithoutCheckListsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCheckListsInput + upsert?: Prisma.UserUpsertWithoutCheckListsInput + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCheckListsInput> +} + +export type UserCreateNestedOneWithoutTemplatesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutTemplatesInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutTemplatesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutTemplatesInput + upsert?: Prisma.UserUpsertWithoutTemplatesInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutTemplatesInput> +} + +export type UserCreateWithoutHabitsInput = { + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + checkIns?: Prisma.CheckInCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateCreateNestedManyWithoutUserInput +} + +export type UserUncheckedCreateWithoutHabitsInput = { + id?: number + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + checkIns?: Prisma.CheckInUncheckedCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListUncheckedCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateUncheckedCreateNestedManyWithoutUserInput +} + +export type UserCreateOrConnectWithoutHabitsInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutHabitsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutHabitsInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutHabitsInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + checkIns?: Prisma.CheckInUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUpdateManyWithoutUserNestedInput +} + +export type UserUncheckedUpdateWithoutHabitsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + checkIns?: Prisma.CheckInUncheckedUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUncheckedUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUncheckedUpdateManyWithoutUserNestedInput +} + +export type UserCreateWithoutCheckInsInput = { + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateCreateNestedManyWithoutUserInput +} + +export type UserUncheckedCreateWithoutCheckInsInput = { + id?: number + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitUncheckedCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListUncheckedCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateUncheckedCreateNestedManyWithoutUserInput +} + +export type UserCreateOrConnectWithoutCheckInsInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutCheckInsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCheckInsInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCheckInsInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUpdateManyWithoutUserNestedInput +} + +export type UserUncheckedUpdateWithoutCheckInsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUncheckedUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUncheckedUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUncheckedUpdateManyWithoutUserNestedInput +} + +export type UserCreateWithoutCheckListsInput = { + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateCreateNestedManyWithoutUserInput +} + +export type UserUncheckedCreateWithoutCheckListsInput = { + id?: number + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitUncheckedCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInUncheckedCreateNestedManyWithoutUserInput + templates?: Prisma.CheckListTemplateUncheckedCreateNestedManyWithoutUserInput +} + +export type UserCreateOrConnectWithoutCheckListsInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutCheckListsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCheckListsInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCheckListsInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUpdateManyWithoutUserNestedInput +} + +export type UserUncheckedUpdateWithoutCheckListsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUncheckedUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUncheckedUpdateManyWithoutUserNestedInput + templates?: Prisma.CheckListTemplateUncheckedUpdateManyWithoutUserNestedInput +} + +export type UserCreateWithoutTemplatesInput = { + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListCreateNestedManyWithoutUserInput +} + +export type UserUncheckedCreateWithoutTemplatesInput = { + id?: number + name: string + email: string + password: string + createdAt?: Date | string + updatedAt?: Date | string + habits?: Prisma.HabitUncheckedCreateNestedManyWithoutUserInput + checkIns?: Prisma.CheckInUncheckedCreateNestedManyWithoutUserInput + checkLists?: Prisma.CheckListUncheckedCreateNestedManyWithoutUserInput +} + +export type UserCreateOrConnectWithoutTemplatesInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutTemplatesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutTemplatesInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutTemplatesInput = { + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUpdateManyWithoutUserNestedInput +} + +export type UserUncheckedUpdateWithoutTemplatesInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + password?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + habits?: Prisma.HabitUncheckedUpdateManyWithoutUserNestedInput + checkIns?: Prisma.CheckInUncheckedUpdateManyWithoutUserNestedInput + checkLists?: Prisma.CheckListUncheckedUpdateManyWithoutUserNestedInput +} + + +/** + * Count Type UserCountOutputType + */ + +export type UserCountOutputType = { + habits: number + checkIns: number + checkLists: number + templates: number +} + +export type UserCountOutputTypeSelect = { + habits?: boolean | UserCountOutputTypeCountHabitsArgs + checkIns?: boolean | UserCountOutputTypeCountCheckInsArgs + checkLists?: boolean | UserCountOutputTypeCountCheckListsArgs + templates?: boolean | UserCountOutputTypeCountTemplatesArgs +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the UserCountOutputType + */ + select?: Prisma.UserCountOutputTypeSelect | null +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountHabitsArgs = { + where?: Prisma.HabitWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountCheckInsArgs = { + where?: Prisma.CheckInWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountCheckListsArgs = { + where?: Prisma.CheckListWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountTemplatesArgs = { + where?: Prisma.CheckListTemplateWhereInput +} export type UserSelect = runtime.Types.Extensions.GetSelect<{ @@ -400,6 +800,11 @@ export type UserSelect + checkIns?: boolean | Prisma.User$checkInsArgs + checkLists?: boolean | Prisma.User$checkListsArgs + templates?: boolean | Prisma.User$templatesArgs + _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs }, ExtArgs["result"]["user"]> @@ -414,10 +819,22 @@ export type UserSelectScalar = { } export type UserOmit = runtime.Types.Extensions.GetOmit<"id" | "name" | "email" | "password" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]> +export type UserInclude = { + habits?: boolean | Prisma.User$habitsArgs + checkIns?: boolean | Prisma.User$checkInsArgs + checkLists?: boolean | Prisma.User$checkListsArgs + templates?: boolean | Prisma.User$templatesArgs + _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs +} export type $UserPayload = { name: "User" - objects: {} + objects: { + habits: Prisma.$HabitPayload[] + checkIns: Prisma.$CheckInPayload[] + checkLists: Prisma.$CheckListPayload[] + templates: Prisma.$CheckListTemplatePayload[] + } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: number name: string @@ -765,6 +1182,10 @@ readonly fields: UserFieldRefs; */ export interface Prisma__UserClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" + habits = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + checkIns = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + checkLists = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + templates = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -816,6 +1237,10 @@ export type UserFindUniqueArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter, which User to fetch. */ @@ -834,6 +1259,10 @@ export type UserFindUniqueOrThrowArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter, which User to fetch. */ @@ -852,6 +1281,10 @@ export type UserFindFirstArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter, which User to fetch. */ @@ -900,6 +1333,10 @@ export type UserFindFirstOrThrowArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter, which User to fetch. */ @@ -948,6 +1385,10 @@ export type UserFindManyArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter, which Users to fetch. */ @@ -996,6 +1437,10 @@ export type UserCreateArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * The data needed to create a User. */ @@ -1025,6 +1470,10 @@ export type UserUpdateArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * The data needed to update a User. */ @@ -1065,6 +1514,10 @@ export type UserUpsertArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * The filter to search for the User to update in case it exists. */ @@ -1091,6 +1544,10 @@ export type UserDeleteArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null /** * Filter which User to delete. */ @@ -1111,6 +1568,102 @@ export type UserDeleteManyArgs = { + /** + * Select specific fields to fetch from the Habit + */ + select?: Prisma.HabitSelect | null + /** + * Omit specific fields from the Habit + */ + omit?: Prisma.HabitOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.HabitInclude | null + where?: Prisma.HabitWhereInput + orderBy?: Prisma.HabitOrderByWithRelationInput | Prisma.HabitOrderByWithRelationInput[] + cursor?: Prisma.HabitWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.HabitScalarFieldEnum | Prisma.HabitScalarFieldEnum[] +} + +/** + * User.checkIns + */ +export type User$checkInsArgs = { + /** + * Select specific fields to fetch from the CheckIn + */ + select?: Prisma.CheckInSelect | null + /** + * Omit specific fields from the CheckIn + */ + omit?: Prisma.CheckInOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckInInclude | null + where?: Prisma.CheckInWhereInput + orderBy?: Prisma.CheckInOrderByWithRelationInput | Prisma.CheckInOrderByWithRelationInput[] + cursor?: Prisma.CheckInWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CheckInScalarFieldEnum | Prisma.CheckInScalarFieldEnum[] +} + +/** + * User.checkLists + */ +export type User$checkListsArgs = { + /** + * Select specific fields to fetch from the CheckList + */ + select?: Prisma.CheckListSelect | null + /** + * Omit specific fields from the CheckList + */ + omit?: Prisma.CheckListOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListInclude | null + where?: Prisma.CheckListWhereInput + orderBy?: Prisma.CheckListOrderByWithRelationInput | Prisma.CheckListOrderByWithRelationInput[] + cursor?: Prisma.CheckListWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CheckListScalarFieldEnum | Prisma.CheckListScalarFieldEnum[] +} + +/** + * User.templates + */ +export type User$templatesArgs = { + /** + * Select specific fields to fetch from the CheckListTemplate + */ + select?: Prisma.CheckListTemplateSelect | null + /** + * Omit specific fields from the CheckListTemplate + */ + omit?: Prisma.CheckListTemplateOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CheckListTemplateInclude | null + where?: Prisma.CheckListTemplateWhereInput + orderBy?: Prisma.CheckListTemplateOrderByWithRelationInput | Prisma.CheckListTemplateOrderByWithRelationInput[] + cursor?: Prisma.CheckListTemplateWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CheckListTemplateScalarFieldEnum | Prisma.CheckListTemplateScalarFieldEnum[] +} + /** * User without action */ @@ -1123,4 +1676,8 @@ export type UserDefaultArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null } diff --git a/package.json b/package.json index 6f6c6bd..b690597 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iboard", - "version": "1.0.0", + "version": "0.2.0", "type": "module", "scripts": { "tauri": "tauri", diff --git a/prisma/migrations/20261022120000_add_checkin_checklist/migration.sql b/prisma/migrations/20261022120000_add_checkin_checklist/migration.sql new file mode 100644 index 0000000..ca4361e --- /dev/null +++ b/prisma/migrations/20261022120000_add_checkin_checklist/migration.sql @@ -0,0 +1,99 @@ +-- CreateTable +CREATE TABLE `Habit` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `userId` INTEGER NOT NULL, + `name` VARCHAR(191) NOT NULL, + `period` VARCHAR(191) NOT NULL DEFAULT 'day', + `target` INTEGER NOT NULL DEFAULT 1, + `color` VARCHAR(191) NOT NULL DEFAULT '#1677ff', + `icon` VARCHAR(191) NOT NULL DEFAULT '', + `note` VARCHAR(191) NOT NULL DEFAULT '', + `archived` BOOLEAN NOT NULL DEFAULT false, + `sortOrder` INTEGER NOT NULL DEFAULT 0, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + INDEX `Habit_userId_archived_sortOrder_idx`(`userId`, `archived`, `sortOrder`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `CheckIn` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `habitId` INTEGER NOT NULL, + `userId` INTEGER NOT NULL, + `date` VARCHAR(10) NOT NULL, + `seq` INTEGER NOT NULL DEFAULT 1, + `note` VARCHAR(191) NOT NULL DEFAULT '', + `source` VARCHAR(191) NOT NULL DEFAULT 'user', + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + UNIQUE INDEX `CheckIn_habitId_date_seq_key`(`habitId`, `date`, `seq`), + INDEX `CheckIn_userId_date_idx`(`userId`, `date`), + INDEX `CheckIn_habitId_date_idx`(`habitId`, `date`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `CheckList` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `userId` INTEGER NOT NULL, + `title` VARCHAR(191) NOT NULL, + `scene` VARCHAR(191) NOT NULL DEFAULT '', + `note` VARCHAR(191) NOT NULL DEFAULT '', + `archived` BOOLEAN NOT NULL DEFAULT false, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + INDEX `CheckList_userId_archived_idx`(`userId`, `archived`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `CheckListItem` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `listId` INTEGER NOT NULL, + `title` VARCHAR(191) NOT NULL, + `note` VARCHAR(191) NOT NULL DEFAULT '', + `done` BOOLEAN NOT NULL DEFAULT false, + `doneAt` DATETIME(3) NULL, + `sortOrder` INTEGER NOT NULL DEFAULT 0, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + INDEX `CheckListItem_listId_sortOrder_idx`(`listId`, `sortOrder`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `CheckListTemplate` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `userId` INTEGER NULL, + `name` VARCHAR(191) NOT NULL, + `scene` VARCHAR(191) NOT NULL DEFAULT '', + `items` JSON NOT NULL, + `builtin` BOOLEAN NOT NULL DEFAULT false, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + INDEX `CheckListTemplate_userId_builtin_idx`(`userId`, `builtin`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `Habit` ADD CONSTRAINT `Habit_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_habitId_fkey` FOREIGN KEY (`habitId`) REFERENCES `Habit`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CheckList` ADD CONSTRAINT `CheckList_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CheckListItem` ADD CONSTRAINT `CheckListItem_listId_fkey` FOREIGN KEY (`listId`) REFERENCES `CheckList`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CheckListTemplate` ADD CONSTRAINT `CheckListTemplate_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c045dd1..64a7361 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,6 +19,12 @@ model User { password String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + // V0.2 打卡 / 清单: 用户删除时级联清理 + habits Habit[] + checkIns CheckIn[] + checkLists CheckList[] + templates CheckListTemplate[] } model Article { @@ -281,4 +287,91 @@ model MihomeEnvHour { @@index([deviceId, hour]) } +// ==================== 打卡 / 日历 / 清单 (V0.2) ==================== + +// 打卡项: 周期性目标 (每天 / 每周 / 每月 / 每年 n 次) +model Habit { + id Int @id @default(autoincrement()) + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + name String + period String @default("day") // day | week | month | year + target Int @default(1) // 周期内目标次数 n (>=1) + color String @default("#1677ff") + icon String @default("") // emoji 或图标名, 预留 + note String @default("") + archived Boolean @default(false) // 归档: 不出现在今日待办, 历史保留 + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + records CheckIn[] + + @@index([userId, archived, sortOrder]) +} + +// 打卡记录: 一次打卡 = 一条; 同日多次 => seq 递增 (1..n, 超额继续累加) +model CheckIn { + id Int @id @default(autoincrement()) + habitId Int + habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade) + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + date String @db.VarChar(10) // 本地日历日 "YYYY-MM-DD" (Asia/Shanghai) + seq Int @default(1) // 当日第几次 + note String @default("") + source String @default("user") // user | backfill (补打) + createdAt DateTime @default(now()) + + @@unique([habitId, date, seq]) + @@index([userId, date]) + @@index([habitId, date]) +} + +// 检查清单: 出差 / 回老家 等场景的一次性清单 +model CheckList { + id Int @id @default(autoincrement()) + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + title String + scene String @default("") // 出差 | 回老家 | 自定义 + note String @default("") + archived Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + items CheckListItem[] + + @@index([userId, archived]) +} + +// 检查项: 勾选即完成 +model CheckListItem { + id Int @id @default(autoincrement()) + listId Int + list CheckList @relation(fields: [listId], references: [id], onDelete: Cascade) + title String + note String @default("") + done Boolean @default(false) + doneAt DateTime? + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([listId, sortOrder]) +} + +// 清单模板: builtin = 系统预置 (userId 为空); 否则为用户自定义 +model CheckListTemplate { + id Int @id @default(autoincrement()) + userId Int? + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + name String + scene String @default("") + items Json // [{ title, note }] + builtin Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([userId, builtin]) +} + diff --git a/readme b/readme index 47f8653..2716367 100644 --- a/readme +++ b/readme @@ -1,27 +1,148 @@ -# iBoard -用于收集和整理经济数据,但是以策略为主体重新组织。 -- 首页主要是放经济文章 -- 数据主要是放各种经济数据和图表。 -- 策略是放按照经济理论组织的相关数据和图表的对比。 - - - -## 首页 -首页显示新闻模块和报警模块的数据,按照时间最新优先,无限滚动,直到数据库中没有数据为止。 - - -### 新闻模块 -增加一个新闻模块,数据来源是每日夜间 3点,通过妙想 MCP 获取国内外最新经济政治新闻,按影响大小排序10条。 -存入数据库。 - - -### 报警模块 -World Bank 中的数据只用来对比妙想 MCP 拉取的数据正确性。当同一时间的同一项数据不同时,记录一个报警。 - -## 监控 -监控分为服务器监控和服务监控两部分。 -服务器监控对接 komari,地址:https://komari.honor3.com/ - +# iBoard +用于收集和整理经济数据,但是以策略为主体重新组织。 + +## 分支规则 + +### 分支类型与职责 + +| 分支 | 职责 | 部署 | 合并方向 | +| --- | --- | --- | --- | +| `master` | **主分支**(生产分支) | ✅ 唯一部署出口,push 即发布生产 | — | +| `dev-*` | **开发集成分支**,如 `dev-0.2` | ❌ 不部署(只跑 CI 校验) | 合到 `master` 后由 master 部署 | +| `tmp-*` | **临时分支**,针对单个问题的修改 | ❌ 不部署 | 合到对应 `dev-*` 后删除 | +| `android-*` | **Android 外壳构建分支**,如 `android-1.0` | ❌ 不部署,只触发 APK 构建 | 构建前须与 `master` 同步 | + +> **关于 `main`**:团队约定里的「main 主分支」在本仓库落地为 **`master`**(仓库默认分支即 `master`,不另建 `main`)。 +> 下文所有「main 主分支」均指 `master`。 + +### 流程 + +``` +tmp-xxx ──合并──▶ dev-0.2 ──合并──▶ master ──push──▶ 自动部署生产 + │ │ + │ └──push──▶ 只跑 CI(版本一致性校验),不部署 + │ + └── 问题解决后删除 + +android-1.0 ── 从 master 同步代码后 push ──▶ 只触发 APK 构建 / 发布到 Packages +``` + +1. **日常开发**:从 `dev-*` 拉 `tmp-*`,改完合回 `dev-*`,`tmp-*` 用完即删。 +2. **上线**:`dev-*` 验收通过后合并到 `master`,**推送 `master` 才部署生产**。 +3. **打 Android 包**:`android-*` 与 `master` 同步(`git merge master` 或 `git reset --hard origin/master`)后推送, + 触发 [android-build.yml](.gitea/workflows/android-build.yml),产物发布到 Gitea Generic Packages。 + +### CI 触发范围 + +| 工作流 | 触发分支 | 动作 | +| --- | --- | --- | +| [docker-build.yml](.gitea/workflows/docker-build.yml) | **仅 `master`** | 构建前端 + 生成签名 OTA 包 + 构建镜像 + 迁移数据库 + 部署生产 | +| [android-build.yml](.gitea/workflows/android-build.yml) | `android-*` | 构建并签名 release APK,上传到 Packages | +| [ota-build.yml](.gitea/workflows/ota-build.yml) | 仅手动 `workflow_dispatch` | 单独构建/签发 OTA 包 | +| [sync-registry.yml](.gitea/workflows/sync-registry.yml) | 见文件 | 同步注册中心 | + +> ⚠️ **重要变更(2026-10)**:`dev-*` 分支**已取消** push 自动部署。 +> 此前 `dev-*` 与 `master` 共用同一套生产环境,推送开发分支等于直接发布,容易把半成品带上线。 +> 现在开发分支的提交只有合并进 `master` 并推送后才会部署。 + +## 版本管理 + +### 一句话规则 + +**同一个版本号,APK(原生包)裸写,OTA 热更包多一个 `-ota` 后缀。** + +``` +分支 dev-0.2 → 版本 0.2.0 + ├─ APK / 桌面安装包 versionName = 0.2.0 (无后缀) + ├─ 前端运行时 APP_VERSION = 0.2.0 (无后缀) + └─ OTA 热更包 version = 0.2.0-ota (多一个 -ota) +``` + +版本号遵循 `主版本.次版本.修订号`,**唯一手动管理点是 `package.json` 的 `version` 字段**(当前 `0.2.0`), +该字段只写裸版本号,**不允许带 `-ota`**——`-ota` 由 `scripts/build-ota.mjs` 打包时自动追加。 + +版本号必须与所在分支对应:`dev-0.2` 分支上就该是 `0.2.x`。 + +### 改版本号时要同步的位置 + +| 文件 | 字段 | 说明 | +| --- | --- | --- | +| `package.json` | `version` | **唯一源**,前端 `src/version.js` 与 OTA 打包脚本均读取此值 | +| `src-tauri/tauri.conf.json` | `version` | Tauri 应用版本 → **APK 的 versionName**、桌面安装包版本 | +| `src-tauri/Cargo.toml` | `[package].version` | Rust 包版本,需与 tauri.conf.json 一致 | +| `src-tauri/Cargo.lock` | `name = "iboard"` 条目 | 同上,随 Cargo.toml 一起改 | + +`scripts/ci/check-version.mjs` 会在 CI 第一段就校验这四处**完全相等**且不含 `-ota`, +并校验 `dev-.` 分支与版本前缀对应;任何一项不满足直接构建失败(`main`/`master` 只跳过分支校验,一致性照样查)。 + +### APK 版本号是怎么来的 + +`tauri android init` 会按 `src-tauri/tauri.conf.json` 的 `version` 生成 +`src-tauri/gen/android/app/tauri.properties`(该目录被 gitignore,CI 每次 `android init` 重新生成): + +``` +tauri.android.versionName = 0.2.0 # 人类可见版本,与 tauri.conf.json 一致 +tauri.android.versionCode = 2000 # 整数,升级判定用 = major*1000000 + minor*1000 + patch +``` + +注意: +- `versionCode` 必须**单调递增**才能覆盖安装,所以版本号只能往上走(`0.2.0` → 2000 > `0.1.0` → 1000)。 +- 本地如果 APK 装完显示旧版本号,是 `src-tauri/gen/android` 缓存没刷新, + 删掉该目录重新 `npx tauri android init` 即可。 + +### 其它 + +- `src/version.js` 从 `package.json` 取值,前端"我"页与 OTA 检查接口展示的即此版本,不要另写死版本号。 +- OTA 升级包的 `version` = `-ota`(见 `scripts/build-ota.mjs`), + 所以版本号错位会直接导致线上 OTA 判新失准。 +- **`min_binary_version` 与这里的版本号是两件事**:它是 OTA 包要求的最低原生二进制版本 + (CI 里由 `OTA_MIN_BINARY` 显式指定,当前 `0.1.0`)。前端版本升到 0.2.0 时它不应跟着抬, + 否则装了 0.1.0 APK 的设备会直接收不到热更。 +- `BUILD_ID` 每次编译自动生成(见 `vite.config.js` 的 `__BUILD_ID__`),无需手动维护。 +- `ota-package/latest.json` 为运行时产物,不纳入版本管理。 + +## 分支与 CI/CD (Gitea Actions) + +分支按版本命名:`dev-0.1`、`dev-0.2`…… 对应 `docs/V<版本>/` 下的需求/设计文档。 +职责与合并方向见上文「分支规则」。 + +| 分支 | 触发的 workflow | 行为 | +| --- | --- | --- | +| `master`(主分支) | `docker-build.yml` | 生产发布:构建前端 → 生成签名 OTA 包 → 打 Docker 镜像 → 数据库自动升级 → 重启容器 `iboard` (宿主 3001) | +| `dev-*` | **不触发部署** | 推送只走版本一致性校验;上线须先合并到 `master` | +| `tmp-*` | 不触发 | 临时分支,合到 `dev-*` 后删除 | +| `android-*` | `android-build.yml` | 打签名 release APK 并上传到 Gitea Generic Package Registry(构建前须与 `master` 同步) | +| 手动 | `ota-build.yml` / `sync-registry.yml` | 单独出 OTA 包 / 同步 secrets 到注册中心 | + +数据库升级约定: +- 唯一入口是 `prisma/migrations/` 下的迁移文件,由 `npx prisma migrate deploy` 在**启新容器之前**执行 + (幂等,无新迁移时为 no-op);迁移失败会中断 workflow,不会用新镜像顶掉仍在运行的旧容器。 +- 由于只有 `master` 会部署,**迁移真正作用到生产库的唯一时机就是 master 发布**; + 开发分支上的迁移不会被自动应用,请在合并到 `master` 前确认可回滚。 +- 本地改了 `prisma/schema.prisma` 后:写迁移 SQL → `npx prisma migrate deploy` 验证 → 提交迁移目录。 + +- 首页主要是放经济文章 +- 数据主要是放各种经济数据和图表。 +- 策略是放按照经济理论组织的相关数据和图表的对比。 + + + +## 首页 +首页显示新闻模块和报警模块的数据,按照时间最新优先,无限滚动,直到数据库中没有数据为止。 + + +### 新闻模块 +增加一个新闻模块,数据来源是每日夜间 3点,通过妙想 MCP 获取国内外最新经济政治新闻,按影响大小排序10条。 +存入数据库。 + + +### 报警模块 +World Bank 中的数据只用来对比妙想 MCP 拉取的数据正确性。当同一时间的同一项数据不同时,记录一个报警。 + +## 监控 +监控分为服务器监控和服务监控两部分。 +服务器监控对接 komari,地址:https://komari.honor3.com/ + ## 前端 OTA 升级 @@ -88,10 +209,25 @@ node scripts/build-ota.mjs # 打成 tar.gz + 签名 + latest.json > **注意:** Tauri 2 的 dev 模式 (`tauri dev`) 会代理到 Vite dev server, **OTA 不生效**. > OTA 仅在 release/打包构建中可用, 测试请用 `tauri build` 产物. +### 客户端判新口径(V0.2 新增) + +"我"页 OTA 组内有一个 **开发版本** 开关(默认关闭),切换"怎么算新版本": + +| 开关 | 判新依据 | 适用 | +| --- | --- | --- | +| 关(默认) | **版本号**逐字段比大小(`0.2.0-ota` > `0.1.0` 才算新) | 生产。旧分支被重建(sequence 变大但版本号更小)时不会把设备拉回旧版 | +| 开 | **build sequence**(服务端 sequence 比本地大就算新) | 开发联调。每次构建都能推到设备 | + +- 开关值持久化在设备端 `localStorage.iboard_ota_dev_mode`(`'1'`/`'0'`),默认 `'0'`。 +- 版本比较在 `src/utils/version.js`(纯函数,`src/api/ota.js` 统一封装两种模式)。 +- **`min_binary_version` 与应用版本是两件事**:前者是前端 bundle 要求的最低原生二进制版本, + 当前 APK 原生层仍是 `0.1.0`,所以 CI 里固定传 `--min-binary 0.1.0`(见 `docker-build.yml` 的 + `OTA_MIN_BINARY`)。只改前端版本号时**不要**跟着抬这个值,否则旧 APK 会收不到更新。 + ### Web 端 Web 浏览器访问 (同源) 时, 服务端返回简化版 manifest, 仅显示"有新版本请刷新". -Web 端不会自动下载包, 也不会替换前端代码. +Web 端不会自动下载包, 也不会替换前端代码. ### CI 自动签名 (Gitea Actions) @@ -111,4 +247,4 @@ otes (可选) **注意**: - CI 用的私钥必须**与** auri.conf.json 里 plugins.hotswap.pubkey **配对**; 换密钥时需要同时更新 CI secret 和 conf. -- 不要把私钥 commit 到 git, 整个 ota-package/hotswap.key 已被 .gitignore 排除. +- 不要把私钥 commit 到 git, 整个 ota-package/hotswap.key 已被 .gitignore 排除. diff --git a/scripts/ci/check-version.mjs b/scripts/ci/check-version.mjs new file mode 100644 index 0000000..847ee95 --- /dev/null +++ b/scripts/ci/check-version.mjs @@ -0,0 +1,115 @@ +#!/usr/bin/env node +// CI 专用: 校验版本号四处一致 + 与分支名一致 +// +// 背景: +// 版本号只允许一个手工管理点 = package.json 的 version, 其余位置全部派生: +// - APK / 桌面安装包 version = (裸版本号, 无后缀) +// - OTA 热更包 version = -ota (多一个 -ota 标记) +// - 前端运行时 APP_VERSION = +// 任何一处漏改都会让线上版本号、APK 版本、OTA 判新三者对不上, 且很难在客户端发现。 +// +// 校验分两段: +// A. 一致性 (任何分支都查): package.json ↔ tauri.conf.json ↔ Cargo.toml ↔ Cargo.lock +// 并要求 package.json 的 version 不含 "-ota" (裸版本, OTA 后缀由打包脚本追加) +// B. 分支对应 (仅 dev-<版本> 分支): +// dev-. → version 必须以 . 开头 +// dev- → version 必须以 . 开头 +// main / master / 其它 → 跳过 +// +// 环境变量: +// GITHUB_REF_NAME - Gitea Actions 提供的分支名 (本地运行可用 BRANCH 覆盖) +// +// 退出码: 0 通过 / 1 不一致 +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const ROOT = path.resolve(__dirname, '..', '..') + +const branch = process.env.GITHUB_REF_NAME || process.env.BRANCH || '' + +function readJson(rel) { + return JSON.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8')) +} + +// Cargo.toml 的 [package].version 位于文件开头, 取第一个行首 version = "x" +function readCargoTomlVersion() { + const text = fs.readFileSync(path.join(ROOT, 'src-tauri/Cargo.toml'), 'utf8') + const m = /^version\s*=\s*"([^"]+)"/m.exec(text) + return m ? m[1] : null +} + +// Cargo.lock 里挑出 name = "iboard" 那个包的 version +function readCargoLockVersion() { + const text = fs.readFileSync(path.join(ROOT, 'src-tauri/Cargo.lock'), 'utf8') + const blocks = text.split(/^\[\[package\]\]$/m) + for (const b of blocks) { + if (/^name\s*=\s*"iboard"$/m.test(b)) { + const m = /^version\s*=\s*"([^"]+)"/m.exec(b) + if (m) return m[1] + } + } + return null +} + +const sources = [ + { file: 'package.json', version: String(readJson('package.json').version || '') }, + { file: 'src-tauri/tauri.conf.json', version: String(readJson('src-tauri/tauri.conf.json').version || '') }, + { file: 'src-tauri/Cargo.toml', version: readCargoTomlVersion() }, + { file: 'src-tauri/Cargo.lock (iboard)', version: readCargoLockVersion() }, +] + +const version = sources[0].version +const otaVersion = version + '-ota' + +console.log('[check-version] 分支: ', branch || '(未知)') +for (const s of sources) { + console.log(`[check-version] ${s.file.padEnd(28)} ${s.version || '(缺失)'}`) +} +console.log('[check-version] 产物版本: APK =', version, '/ OTA =', otaVersion) + +let failed = false + +// ---- A. 四处一致性 + 裸版本号 ---- +if (/-ota$/i.test(version)) { + console.error('') + console.error(`[check-version] package.json 的 version 不能带 "-ota": ${version}`) + console.error('[check-version] OTA 后缀由 scripts/build-ota.mjs 自动追加, 请只写裸版本号 (如 0.2.0)') + failed = true +} + +const mismatched = sources.filter((s) => s.version !== version) +if (mismatched.length) { + console.error('') + console.error('============================================================') + console.error('[check-version] 版本号不一致, 构建中止') + console.error('============================================================') + for (const s of mismatched) { + console.error(` ${s.file}: ${s.version || '(缺失)'} ≠ package.json: ${version}`) + } + console.error('') + console.error(' 规则: APK / 桌面包版本 = 裸版本号 (无后缀);') + console.error(' OTA 热更包版本 = 同一个版本号 + "-ota"。') + console.error(' 改版本时四处必须一起改 (见 readme「版本管理」)。') + failed = true +} + +// ---- B. 分支对应 ---- +const m = /^dev-(\d+)(?:\.(\d+))?$/.exec(branch) +if (!m) { + console.log('[check-version] 非 dev-<版本> 分支, 跳过分支对应校验') +} else { + const want = m[2] === undefined ? `${m[1]}.` : `${m[1]}.${m[2]}` + if (!version.startsWith(want)) { + console.error('') + console.error(`[check-version] 版本与分支不一致: 分支 "${branch}" 要求版本以 ${want} 开头, 实际是 ${version}`) + console.error('[check-version] 请修改 package.json 的 version, 并同步 tauri.conf.json / Cargo.toml / Cargo.lock') + failed = true + } else { + console.log(`[check-version] 分支一致 (${branch} ↔ ${version})`) + } +} + +if (failed) process.exit(1) +console.log('[check-version] 全部通过') diff --git a/server/checkin/period.js b/server/checkin/period.js new file mode 100644 index 0000000..4f958c0 --- /dev/null +++ b/server/checkin/period.js @@ -0,0 +1,120 @@ +// 打卡周期计算: 全部以 Asia/Shanghai 的"本地日历日"为准 +// 日期统一用 "YYYY-MM-DD" 字符串 (字典序 = 时间序, 可直接用于范围查询) + +export const TZ = 'Asia/Shanghai' + +const FMT = new Intl.DateTimeFormat('en-CA', { + timeZone: TZ, + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + +/** 任意时间 -> 本地日历日 "YYYY-MM-DD" */ +export function toDateStr(d = new Date()) { + return FMT.format(d instanceof Date ? d : new Date(d)) +} + +/** "YYYY-MM-DD" -> UTC 零点的 Date (仅用于日期运算, 不做时区展示) */ +function toUTC(s) { + const [y, m, d] = String(s).split('-').map(Number) + return new Date(Date.UTC(y, (m || 1) - 1, d || 1)) +} + +function fromUTC(dt) { + return dt.toISOString().slice(0, 10) +} + +export function addDays(s, n) { + const d = toUTC(s) + d.setUTCDate(d.getUTCDate() + n) + return fromUTC(d) +} + +export function addMonths(s, n) { + const [y, m] = String(s).split('-').map(Number) + const first = new Date(Date.UTC(y, (m || 1) - 1 + n, 1)) + const day = Number(String(s).slice(8, 10)) || 1 + const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate() + first.setUTCDate(Math.min(day, lastDay)) + return fromUTC(first) +} + +/** 周一为一周第一天 */ +export function startOfWeek(s) { + const dow = (toUTC(s).getUTCDay() + 6) % 7 // 0=周一 + return addDays(s, -dow) +} + +export function endOfWeek(s) { + return addDays(startOfWeek(s), 6) +} + +export function startOfMonth(s) { + return String(s).slice(0, 8) + '01' +} + +export function endOfMonth(s) { + const [y, m] = String(s).split('-').map(Number) + return fromUTC(new Date(Date.UTC(y, m || 1, 0))) +} + +export function startOfYear(s) { + return String(s).slice(0, 4) + '-01-01' +} + +export function endOfYear(s) { + return String(s).slice(0, 4) + '-12-31' +} + +export const PERIODS = ['day', 'week', 'month', 'year'] + +export const PERIOD_LABEL = { day: '每天', week: '每周', month: '每月', year: '每年' } + +/** 周期窗口 [start, end] (闭区间, 本地日历日) */ +export function periodRange(period, s) { + switch (period) { + case 'week': return { start: startOfWeek(s), end: endOfWeek(s) } + case 'month': return { start: startOfMonth(s), end: endOfMonth(s) } + case 'year': return { start: startOfYear(s), end: endOfYear(s) } + default: return { start: s, end: s } + } +} + +/** 周期窗口内的天数 */ +export function periodDays(period, s) { + const { start, end } = periodRange(period, s) + return Math.round((toUTC(end) - toUTC(start)) / 86400000) + 1 +} + +/** + * 日均期望次数: 用于日历上"某一天是否完成了当天应做的份额" + * - 每天 n 次 -> n + * - 每周 n 次 -> ceil(n / 7) + * - 每月 n 次 -> ceil(n / 当月天数) + * - 每年 n 次 -> ceil(n / 365) + * 环形进度仍以"周期完成度"(周期内累计次数 / n) 为准, 两者不冲突 + */ +export function expectedPerDay(period, target, s) { + const n = Math.max(1, Number(target) || 1) + if (period === 'week') return Math.max(1, Math.ceil(n / 7)) + if (period === 'month') return Math.max(1, Math.ceil(n / periodDays('month', s))) + if (period === 'year') return Math.max(1, Math.ceil(n / 365)) + return n +} + +/** 该周期在日期 d 之前是否已完整结束 (用于 streak 判断: 未结束的周期不算断签) */ +export function periodIsClosed(period, d, today) { + const { end } = periodRange(period, d) + return end < today +} + +/** 遍历 [start, end] 闭区间的每一天 */ +export function eachDay(start, end, fn) { + let cur = start + let guard = 0 + while (cur <= end && guard++ < 4000) { + fn(cur) + cur = addDays(cur, 1) + } +} diff --git a/server/index.js b/server/index.js index 60372a3..814d00c 100644 --- a/server/index.js +++ b/server/index.js @@ -12,6 +12,8 @@ import otaRouter from './routes/ota.js' import apizeroRouter from './routes/apizero.js' import monitorsRouter from './routes/monitors.js' import mihomeRouter from './routes/mihome.js' +import checkinRouter from './routes/checkin.js' +import checklistsRouter from './routes/checklists.js' const PORT = Number(process.env.PORT) || 3001 const DIST_DIR = path.join(process.cwd(), 'dist') @@ -36,6 +38,9 @@ app.use('/api', articlesRouter) app.use('/api', apizeroRouter) app.use('/api', monitorsRouter) app.use('/api/mihome', mihomeRouter) +// V0.2 打卡 / 日历 / 清单 +app.use('/api/checkin', checkinRouter) +app.use('/api/checklists', checklistsRouter) // 静态资源 (Vite build 产物) if (HAS_DIST) { diff --git a/server/routes/checkin.js b/server/routes/checkin.js new file mode 100644 index 0000000..2c41ccb --- /dev/null +++ b/server/routes/checkin.js @@ -0,0 +1,328 @@ +import { Router } from 'express' +import { prisma } from '../db.js' +import { authRequired } from '../middleware.js' +import { + toDateStr, addDays, periodRange, periodDays, expectedPerDay, + periodIsClosed, eachDay, PERIODS, PERIOD_LABEL, +} from '../checkin/period.js' + +const router = Router() +router.use(authRequired) + +// 补打卡允许的回溯天数 (F-CK-10) +const BACKFILL_DAYS = 7 + +const DATE_RE = /^\d{4}-\d{2}-\d{2}$/ +const MONTH_RE = /^\d{4}-\d{2}$/ + +function normDate(v) { + const s = String(v || '') + return DATE_RE.test(s) ? s : toDateStr() +} + +// ---------- 统计辅助 ---------- + +// 记录 -> { habitId: { date: count } } +function groupRecords(records) { + const map = new Map() + for (const r of records) { + if (!map.has(r.habitId)) map.set(r.habitId, new Map()) + const byDate = map.get(r.habitId) + byDate.set(r.date, (byDate.get(r.date) || 0) + 1) + } + return map +} + +function countInRange(byDate, start, end) { + let n = 0 + eachDay(start, end, (d) => { n += byDate.get(d) || 0 }) + return n +} + +// 连续达标的周期数: 当前周期未结束且未达标 -> 跳过(不算断签), 不是中断 +function computeStreak(habit, byDate, today) { + let cursor = today + let streak = 0 + for (let i = 0; i < 400; i++) { + const { start } = periodRange(habit.period, cursor) + const done = countInRange(byDate, start, periodRange(habit.period, cursor).end) + if (done >= habit.target) { + streak++ + } else if (periodIsClosed(habit.period, cursor, today)) { + break + } + const prevEnd = addDays(start, -1) + if (prevEnd < '1970-01-01') break + cursor = prevEnd + } + return streak +} + +async function loadActiveHabits(userId) { + return prisma.habit.findMany({ + where: { userId, archived: false }, + orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }], + }) +} + +// 组装单个打卡项的展示态 +function buildItem(habit, byDate, date, today) { + const { start, end } = periodRange(habit.period, date) + const done = countInRange(byDate, start, end) + const target = Math.max(1, habit.target) + const onDate = byDate.get(date) || 0 + const expPerDay = expectedPerDay(habit.period, target, date) + return { + id: habit.id, + name: habit.name, + period: habit.period, + periodLabel: PERIOD_LABEL[habit.period] || '每天', + target, + color: habit.color, + icon: habit.icon, + note: habit.note, + archived: habit.archived, + periodStart: start, + periodEnd: end, + periodDays: periodDays(habit.period, date), + done, + remaining: Math.max(0, target - done), + percent: Math.min(1, target > 0 ? done / target : 1), + completed: done >= target, + over: Math.max(0, done - target), + countOnDate: onDate, + expectedPerDay: expPerDay, + dayDone: onDate >= expPerDay, + streak: computeStreak(habit, byDate, today), + } +} + +// 拉取 [start, end] 内的打卡记录并分组 +async function loadRecords(userId, habits, ranges) { + let min = ranges[0]?.start + let max = ranges[0]?.end + for (const r of ranges) { + if (r.start < min) min = r.start + if (r.end > max) max = r.end + } + if (!min || !max) return new Map() + const records = await prisma.checkIn.findMany({ + where: { userId, habitId: { in: habits.map((h) => h.id) }, date: { gte: min, lte: max } }, + select: { habitId: true, date: true }, + }) + return groupRecords(records) +} + +// ---------- 打卡项 ---------- + +// GET /api/checkin/habits?date=YYYY-MM-DD 今日(或指定日)待办 + 周期进度 +router.get('/habits', async (req, res, next) => { + try { + const userId = req.user.sub + const today = toDateStr() + const date = normDate(req.query.date) + const habits = await loadActiveHabits(userId) + const ranges = habits.map((h) => periodRange(h.period, date)) + const grouped = await loadRecords(userId, habits, ranges.length ? ranges : [{ start: date, end: date }]) + const items = habits.map((h) => buildItem(h, grouped.get(h.id) || new Map(), date, today)) + const doneCount = items.filter((i) => i.completed).length + res.json({ today, date, items, summary: { total: items.length, done: doneCount } }) + } catch (e) { next(e) } +}) + +// POST /api/checkin/habits 新建打卡项 +router.post('/habits', async (req, res, next) => { + try { + const userId = req.user.sub + const { name, period = 'day', target = 1, color = '#1677ff', icon = '', note = '' } = req.body || {} + const cleanName = String(name || '').trim() + if (!cleanName) return res.status(400).json({ error: '名称不能为空' }) + if (cleanName.length > 20) return res.status(400).json({ error: '名称不能超过 20 个字' }) + if (!PERIODS.includes(period)) return res.status(400).json({ error: '周期类型不合法' }) + const t = Math.floor(Number(target)) + if (!Number.isFinite(t) || t < 1) return res.status(400).json({ error: '目标次数需为 ≥1 的整数' }) + const row = await prisma.habit.create({ + data: { userId, name: cleanName, period, target: t, color: String(color), icon: String(icon), note: String(note || '') }, + }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// PUT /api/checkin/habits/:id 编辑 +router.put('/habits/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.habit.findFirst({ where: { id, userId } }) + if (!exist) return res.status(404).json({ error: '打卡项不存在' }) + const { name, period, target, color, icon, note, archived, sortOrder } = req.body || {} + const data = {} + if (name !== undefined) { + const n = String(name).trim() + if (!n) return res.status(400).json({ error: '名称不能为空' }) + if (n.length > 20) return res.status(400).json({ error: '名称不能超过 20 个字' }) + data.name = n + } + if (period !== undefined) { + if (!PERIODS.includes(period)) return res.status(400).json({ error: '周期类型不合法' }) + data.period = period + } + if (target !== undefined) { + const t = Math.floor(Number(target)) + if (!Number.isFinite(t) || t < 1) return res.status(400).json({ error: '目标次数需为 ≥1 的整数' }) + data.target = t + } + if (color !== undefined) data.color = String(color) + if (icon !== undefined) data.icon = String(icon) + if (note !== undefined) data.note = String(note) + if (archived !== undefined) data.archived = !!archived + if (sortOrder !== undefined) data.sortOrder = Number(sortOrder) || 0 + const row = await prisma.habit.update({ where: { id }, data }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// DELETE /api/checkin/habits/:id?hard=1 默认归档(保留历史), hard=1 物理删除 +router.delete('/habits/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.habit.findFirst({ where: { id, userId } }) + if (!exist) return res.status(404).json({ error: '打卡项不存在' }) + if (req.query.hard === '1') { + await prisma.checkIn.deleteMany({ where: { habitId: id } }) + await prisma.habit.delete({ where: { id } }) + return res.json({ ok: true, removed: true }) + } + const row = await prisma.habit.update({ where: { id }, data: { archived: true } }) + res.json({ ok: true, removed: false, item: row }) + } catch (e) { next(e) } +}) + +// ---------- 打卡 / 撤销 ---------- + +// POST /api/checkin/habits/:id/check { date?, note? } 打一次卡 +router.post('/habits/:id/check', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const habit = await prisma.habit.findFirst({ where: { id, userId } }) + if (!habit) return res.status(404).json({ error: '打卡项不存在' }) + const today = toDateStr() + const date = normDate((req.body || {}).date) + if (date > today) return res.status(400).json({ error: '不能为未来日期打卡' }) + if (date < addDays(today, -BACKFILL_DAYS)) { + return res.status(400).json({ error: `最多只能补打最近 ${BACKFILL_DAYS} 天` }) + } + const dayRecords = await prisma.checkIn.findMany({ where: { habitId: id, date }, select: { seq: true } }) + const seq = dayRecords.reduce((m, r) => Math.max(m, r.seq), 0) + 1 + const note = String((req.body || {}).note || '').slice(0, 200) + await prisma.checkIn.create({ + data: { habitId: id, userId, date, seq, note, source: date === today ? 'user' : 'backfill' }, + }) + const grouped = await loadRecords(userId, [habit], [periodRange(habit.period, date)]) + res.json({ item: buildItem(habit, grouped.get(habit.id) || new Map(), date, today), date }) + } catch (e) { + // 唯一约束冲突 = 并发重复提交, 视为已打卡 + if (e && e.code === 'P2002') return res.status(409).json({ error: '该次打卡已记录' }) + next(e) + } +}) + +// DELETE /api/checkin/habits/:id/check?date= 撤销本周期内最近一次打卡 +router.delete('/habits/:id/check', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const habit = await prisma.habit.findFirst({ where: { id, userId } }) + if (!habit) return res.status(404).json({ error: '打卡项不存在' }) + const today = toDateStr() + const date = normDate(req.query.date) + const { start, end } = periodRange(habit.period, date) + const last = await prisma.checkIn.findFirst({ + where: { habitId: id, date: { gte: start, lte: end } }, + orderBy: [{ date: 'desc' }, { seq: 'desc' }], + }) + if (!last) return res.status(400).json({ error: '本周期还没有打卡记录' }) + await prisma.checkIn.delete({ where: { id: last.id } }) + const grouped = await loadRecords(userId, [habit], [{ start, end }]) + res.json({ item: buildItem(habit, grouped.get(habit.id) || new Map(), date, today), date }) + } catch (e) { next(e) } +}) + +// ---------- 某日详情 / 月历 ---------- + +// GET /api/checkin/day?date=YYYY-MM-DD 当天每个打卡项的明细 (含可打卡/只读标记) +router.get('/day', async (req, res, next) => { + try { + const userId = req.user.sub + const today = toDateStr() + const date = normDate(req.query.date) + const habits = await loadActiveHabits(userId) + const ranges = habits.map((h) => periodRange(h.period, date)) + const grouped = await loadRecords(userId, habits, ranges.length ? ranges : [{ start: date, end: date }]) + const items = habits.map((h) => { + const base = buildItem(h, grouped.get(h.id) || new Map(), date, today) + return { ...base, canCheck: date <= today && date >= addDays(today, -BACKFILL_DAYS) } + }) + res.json({ today, date, isToday: date === today, isFuture: date > today, items }) + } catch (e) { next(e) } +}) + +// GET /api/checkin/calendar?month=YYYY-MM 月历: 每天完成情况 + 当月完成率 +router.get('/calendar', async (req, res, next) => { + try { + const userId = req.user.sub + const today = toDateStr() + const month = MONTH_RE.test(String(req.query.month || '')) ? String(req.query.month) : today.slice(0, 7) + const start = month + '-01' + const end = addDays(monthStartNext(month), -1) + const habits = await prisma.habit.findMany({ where: { userId }, orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] }) + const active = habits.filter((h) => !h.archived) + const records = await prisma.checkIn.findMany({ + where: { userId, date: { gte: start, lte: end } }, + select: { habitId: true, date: true }, + }) + const grouped = groupRecords(records) + const days = [] + let expTotal = 0 + let doneTotal = 0 + eachDay(start, end, (d) => { + let expected = 0 + let actual = 0 + let over = false + const detail = [] + for (const h of active) { + if (toDateStr(h.createdAt) > d) continue // 创建之前的日期不计入期望 + const exp = expectedPerDay(h.period, Math.max(1, h.target), d) + const cnt = (grouped.get(h.id) || new Map()).get(d) || 0 + expected += exp + actual += Math.min(cnt, exp) + if (cnt > exp) over = true + detail.push({ id: h.id, name: h.name, color: h.color, count: cnt, expected: exp, done: cnt >= exp }) + } + const isFuture = d > today + if (!isFuture) { expTotal += expected; doneTotal += actual } + let status = 'empty' + if (expected > 0 && actual >= expected) status = over ? 'over' : 'full' + else if (actual > 0) status = 'partial' + days.push({ + date: d, expected, actual, status, isToday: d === today, isFuture, + habits: detail, + }) + }) + res.json({ + today, month, days, + rate: expTotal > 0 ? Math.min(1, doneTotal / expTotal) : 0, + habits: active.map((h) => ({ id: h.id, name: h.name, color: h.color, period: h.period, target: h.target })), + }) + } catch (e) { next(e) } +}) + +function monthStartNext(month) { + const [y, m] = month.split('-').map(Number) + const d = new Date(Date.UTC(y, m, 1)) // m 已 +1 (下月) + return d.toISOString().slice(0, 10) +} + +export default router diff --git a/server/routes/checklists.js b/server/routes/checklists.js new file mode 100644 index 0000000..76e3558 --- /dev/null +++ b/server/routes/checklists.js @@ -0,0 +1,356 @@ +import { Router } from 'express' +import { prisma } from '../db.js' +import { authRequired } from '../middleware.js' + +const router = Router() +router.use(authRequired) + +// 系统预置模板 (builtin): 首次访问模板接口时幂等写入 +const BUILTIN_TEMPLATES = [ + { + name: '出差', + scene: '出差', + items: [ + { title: '身份证 / 证件' }, + { title: '手机充电器 + 充电宝' }, + { title: '笔记本电脑 + 电源' }, + { title: '耳机' }, + { title: '换洗衣物' }, + { title: '洗漱包' }, + { title: '常用药' }, + { title: '雨伞' }, + { title: '车票 / 机票 / 酒店确认' }, + { title: '工牌 / 名片' }, + { title: '报销单据' }, + ], + }, + { + name: '回老家', + scene: '回老家', + items: [ + { title: '身份证 / 证件' }, + { title: '钥匙' }, + { title: '手机充电器 + 充电宝' }, + { title: '换洗衣物' }, + { title: '洗漱包' }, + { title: '常用药' }, + { title: '给家人的礼物 / 伴手礼' }, + { title: '长辈的药品 / 营养品' }, + { title: '口罩' }, + { title: '现金零钱' }, + { title: '车票 / 加油 / 行程确认' }, + { title: '家里门窗水电检查' }, + ], + }, +] + +async function ensureBuiltin() { + const count = await prisma.checkListTemplate.count({ where: { builtin: true } }) + if (count >= BUILTIN_TEMPLATES.length) return + for (const t of BUILTIN_TEMPLATES) { + const exist = await prisma.checkListTemplate.findFirst({ where: { builtin: true, name: t.name } }) + if (exist) continue + await prisma.checkListTemplate.create({ + data: { name: t.name, scene: t.scene, items: t.items, builtin: true, userId: null }, + }) + } +} + +const LIST_INCLUDE = { items: { orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] } } + +function withProgress(list) { + const items = list.items || [] + const done = items.filter((i) => i.done).length + return { + ...list, + items, + total: items.length, + done, + percent: items.length > 0 ? done / items.length : 0, + completed: items.length > 0 && done === items.length, + } +} + +// ---------- 模板 ---------- + +// 模板按名称字母顺序 (中文按拼音) 排序, 前后端共用同一比较器语义 +const NAME_COLLATOR = new Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' }) +const byName = (a, b) => NAME_COLLATOR.compare(String(a.name || ''), String(b.name || '')) + +// GET /api/checklists/templates 模板列表 (含系统预置, 按字母顺序) +router.get('/templates', async (_req, res, next) => { + try { + await ensureBuiltin() + const rows = await prisma.checkListTemplate.findMany({ + where: { OR: [{ builtin: true }, { userId: _req.user.sub }] }, + }) + res.json({ items: rows.sort(byName) }) + } catch (e) { next(e) } +}) + +// GET /api/checklists/templates/:id 模板详情 +router.get('/templates/:id', async (req, res, next) => { + try { + const row = await prisma.checkListTemplate.findFirst({ + where: { id: Number(req.params.id), OR: [{ builtin: true }, { userId: req.user.sub }] }, + }) + if (!row) return res.status(404).json({ error: '模板不存在' }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// POST /api/checklists/templates 另存为模板 { name, scene?, items: [{title, note}] } +router.post('/templates', async (req, res, next) => { + try { + const userId = req.user.sub + const { name, scene = '', items = [] } = req.body || {} + const cleanName = String(name || '').trim() + if (!cleanName) return res.status(400).json({ error: '模板名称不能为空' }) + if (cleanName.length > 20) return res.status(400).json({ error: '模板名称不能超过 20 个字' }) + const list = (Array.isArray(items) ? items : []) + .map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') })) + .filter((i) => i.title) + if (!list.length) return res.status(400).json({ error: '模板至少需要一个检查项' }) + const row = await prisma.checkListTemplate.create({ + data: { userId, name: cleanName, scene: String(scene), items: list, builtin: false }, + }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// PUT /api/checklists/templates/:id 改名称 / 检查项 (系统预置模板不可改) +router.put('/templates/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkListTemplate.findFirst({ where: { id, userId, builtin: false } }) + if (!exist) return res.status(404).json({ error: '模板不存在、无权限修改或是系统预置模板' }) + const { name, scene, items } = req.body || {} + const data = {} + if (name !== undefined) { + const n = String(name).trim() + if (!n) return res.status(400).json({ error: '模板名称不能为空' }) + if (n.length > 20) return res.status(400).json({ error: '模板名称不能超过 20 个字' }) + data.name = n + } + if (scene !== undefined) data.scene = String(scene) + if (items !== undefined) { + const list = (Array.isArray(items) ? items : []) + .map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') })) + .filter((i) => i.title) + if (!list.length) return res.status(400).json({ error: '模板至少需要一个检查项' }) + data.items = list + } + const row = await prisma.checkListTemplate.update({ where: { id }, data }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// DELETE /api/checklists/templates/:id 仅自建模板可删 +router.delete('/templates/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkListTemplate.findFirst({ where: { id, userId, builtin: false } }) + if (!exist) return res.status(404).json({ error: '模板不存在或无权删除' }) + await prisma.checkListTemplate.delete({ where: { id } }) + res.json({ ok: true }) + } catch (e) { next(e) } +}) + +// ---------- 清单 ---------- + +// GET /api/checklists?archived=0|1 +router.get('/', async (req, res, next) => { + try { + const userId = req.user.sub + const archived = String(req.query.archived || '0') === '1' + const rows = await prisma.checkList.findMany({ + where: { userId, archived }, + include: LIST_INCLUDE, + orderBy: [{ updatedAt: 'desc' }], + }) + res.json({ items: rows.map(withProgress) }) + } catch (e) { next(e) } +}) + +// POST /api/checklists { title, scene?, note?, items?: string[] | [{title,note}], templateId? } +router.post('/', async (req, res, next) => { + try { + const userId = req.user.sub + const { title, scene = '', note = '', items = [], templateId } = req.body || {} + const cleanTitle = String(title || '').trim() + if (!cleanTitle) return res.status(400).json({ error: '清单名称不能为空' }) + + let seed = (Array.isArray(items) ? items : []) + .map((i) => (typeof i === 'string' ? { title: i } : i)) + .map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') })) + .filter((i) => i.title) + + if (!seed.length && templateId) { + const tpl = await prisma.checkListTemplate.findFirst({ + where: { id: Number(templateId), OR: [{ builtin: true }, { userId }] }, + }) + if (tpl && Array.isArray(tpl.items)) { + seed = tpl.items.map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') })).filter((i) => i.title) + } + } + + const row = await prisma.checkList.create({ + data: { + userId, + title: cleanTitle, + scene: String(scene), + note: String(note), + items: { create: seed.map((i, idx) => ({ title: i.title, note: i.note, sortOrder: idx })) }, + }, + include: LIST_INCLUDE, + }) + res.json({ item: withProgress(row) }) + } catch (e) { next(e) } +}) + +// GET /api/checklists/:id +router.get('/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const row = await prisma.checkList.findFirst({ where: { id, userId }, include: LIST_INCLUDE }) + if (!row) return res.status(404).json({ error: '清单不存在' }) + res.json({ item: withProgress(row) }) + } catch (e) { next(e) } +}) + +// PUT /api/checklists/:id { title?, scene?, note?, archived? } +router.put('/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkList.findFirst({ where: { id, userId } }) + if (!exist) return res.status(404).json({ error: '清单不存在' }) + const { title, scene, note, archived } = req.body || {} + const data = {} + if (title !== undefined) { + const t = String(title).trim() + if (!t) return res.status(400).json({ error: '清单名称不能为空' }) + data.title = t + } + if (scene !== undefined) data.scene = String(scene) + if (note !== undefined) data.note = String(note) + if (archived !== undefined) data.archived = !!archived + const row = await prisma.checkList.update({ where: { id }, data, include: LIST_INCLUDE }) + res.json({ item: withProgress(row) }) + } catch (e) { next(e) } +}) + +// DELETE /api/checklists/:id +router.delete('/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkList.findFirst({ where: { id, userId } }) + if (!exist) return res.status(404).json({ error: '清单不存在' }) + await prisma.checkListItem.deleteMany({ where: { listId: id } }) + await prisma.checkList.delete({ where: { id } }) + res.json({ ok: true }) + } catch (e) { next(e) } +}) + +// POST /api/checklists/:id/duplicate 复制一份新清单 (勾选状态重置) +router.post('/:id/duplicate', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const src = await prisma.checkList.findFirst({ where: { id, userId }, include: LIST_INCLUDE }) + if (!src) return res.status(404).json({ error: '清单不存在' }) + const row = await prisma.checkList.create({ + data: { + userId, + title: src.title + ' 副本', + scene: src.scene, + note: src.note, + items: { + create: src.items.map((i, idx) => ({ title: i.title, note: i.note, done: false, sortOrder: idx })), + }, + }, + include: LIST_INCLUDE, + }) + res.json({ item: withProgress(row) }) + } catch (e) { next(e) } +}) + +// ---------- 检查项 ---------- + +// POST /api/checklists/:id/items { title, note? } +router.post('/:id/items', async (req, res, next) => { + try { + const userId = req.user.sub + const listId = Number(req.params.id) + const list = await prisma.checkList.findFirst({ where: { id: listId, userId } }) + if (!list) return res.status(404).json({ error: '清单不存在' }) + const title = String((req.body || {}).title || '').trim() + if (!title) return res.status(400).json({ error: '检查项不能为空' }) + if (title.length > 40) return res.status(400).json({ error: '检查项不能超过 40 个字' }) + const max = await prisma.checkListItem.findFirst({ where: { listId }, orderBy: { sortOrder: 'desc' } }) + const row = await prisma.checkListItem.create({ + data: { listId, title, note: String((req.body || {}).note || ''), sortOrder: (max?.sortOrder || 0) + 1 }, + }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// PUT /api/checklists/items/:id { title?, note?, done?, sortOrder? } +router.put('/items/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkListItem.findFirst({ where: { id, list: { userId } } }) + if (!exist) return res.status(404).json({ error: '检查项不存在' }) + const { title, note, done, sortOrder } = req.body || {} + const data = {} + if (title !== undefined) { + const t = String(title).trim() + if (!t) return res.status(400).json({ error: '检查项不能为空' }) + data.title = t + } + if (note !== undefined) data.note = String(note) + if (sortOrder !== undefined) data.sortOrder = Number(sortOrder) || 0 + if (done !== undefined) { + data.done = !!done + data.doneAt = done ? new Date() : null + } + const row = await prisma.checkListItem.update({ where: { id }, data }) + res.json({ item: row }) + } catch (e) { next(e) } +}) + +// DELETE /api/checklists/items/:id +router.delete('/items/:id', async (req, res, next) => { + try { + const userId = req.user.sub + const id = Number(req.params.id) + const exist = await prisma.checkListItem.findFirst({ where: { id, list: { userId } } }) + if (!exist) return res.status(404).json({ error: '检查项不存在' }) + await prisma.checkListItem.delete({ where: { id } }) + res.json({ ok: true }) + } catch (e) { next(e) } +}) + +// POST /api/checklists/:id/check-all { done: boolean } 一键全选 / 全不选 +router.post('/:id/check-all', async (req, res, next) => { + try { + const userId = req.user.sub + const listId = Number(req.params.id) + const list = await prisma.checkList.findFirst({ where: { id: listId, userId } }) + if (!list) return res.status(404).json({ error: '清单不存在' }) + const done = !!(req.body || {}).done + await prisma.checkListItem.updateMany({ + where: { listId }, + data: { done, doneAt: done ? new Date() : null }, + }) + const row = await prisma.checkList.findFirst({ where: { id: listId }, include: LIST_INCLUDE }) + res.json({ item: withProgress(row) }) + } catch (e) { next(e) } +}) + +export default router diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7f1a392..536cfbb 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1456,7 +1456,7 @@ dependencies = [ [[package]] name = "iboard" -version = "0.1.0" +version = "0.2.0" dependencies = [ "serde", "serde_json", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 71f3739..f19b1ee 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iboard" -version = "0.1.0" +version = "0.2.0" description = "iBoard - 经济数据与策略" authors = ["iBoard Team"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index cc20d44..7b82045 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "iBoard", - "version": "0.1.0", + "version": "0.2.0", "identifier": "com.iboard.app", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/App.jsx b/src/App.jsx index b76aede..e0e8763 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,19 +1,21 @@ import { Routes, Route, Navigate, useLocation } from 'react-router-dom' import MobileShell from './layouts/MobileShell.jsx' import LoginPage from './pages/LoginPage.jsx' -import HomePage from './pages/HomePage.jsx' -import NewsDetail from './pages/news/NewsDetail.jsx' import MacroPage from './pages/MacroPage.jsx' import MonitorPage from './pages/MonitorPage.jsx' import ServerDetail from './pages/monitor/ServerDetail.jsx' import MePage from './pages/MePage.jsx' +import CheckinPage from './pages/checkin/CheckinPage.jsx' import { useAuth } from './auth/AuthContext.jsx' -// 已登录访问 /login 自动回首页 +// 默认落地页: 打卡 (V0.2 起打卡是第一个 Tab 且为默认选中) +const HOME_PATH = '/checkin' + +// 已登录访问 /login 自动回默认页 function RedirectIfAuthed({ children }) { const { user, loading } = useAuth() if (loading) return null - if (user) return + if (user) return return children } @@ -31,14 +33,17 @@ export default function App() { } /> }> - } /> - } /> + {/* 打卡为默认首页 */} + } /> + } /> + {/* 宏观: 报警 / 新闻 / 油价 / 经济数据 / 策略研究 / 经济文章 (V0.2 原首页并入, 默认新闻) */} + } /> } /> } /> } /> } /> - } /> + } /> ) } \ No newline at end of file diff --git a/src/api/checkin.js b/src/api/checkin.js new file mode 100644 index 0000000..e31c020 --- /dev/null +++ b/src/api/checkin.js @@ -0,0 +1,41 @@ +import { api } from './client.js' + +const q = (params) => { + const s = new URLSearchParams() + Object.entries(params || {}).forEach(([k, v]) => { + if (v !== undefined && v !== null && v !== '') s.set(k, v) + }) + const str = s.toString() + return str ? '?' + str : '' +} + +// 打卡 / 日历 +export const checkinApi = { + habits: (date) => api('/checkin/habits' + q({ date })), + day: (date) => api('/checkin/day' + q({ date })), + calendar: (month) => api('/checkin/calendar' + q({ month })), + createHabit: (body) => api('/checkin/habits', { method: 'POST', body }), + updateHabit: (id, body) => api(`/checkin/habits/${id}`, { method: 'PUT', body }), + deleteHabit: (id, hard) => api(`/checkin/habits/${id}${hard ? '?hard=1' : ''}`, { method: 'DELETE' }), + check: (id, date, note) => api(`/checkin/habits/${id}/check`, { method: 'POST', body: { date, note } }), + uncheck: (id, date) => api(`/checkin/habits/${id}/check` + q({ date }), { method: 'DELETE' }), +} + +// 检查清单 +export const checklistApi = { + lists: (archived) => api('/checklists' + q({ archived: archived ? 1 : 0 })), + list: (id) => api(`/checklists/${id}`), + create: (body) => api('/checklists', { method: 'POST', body }), + update: (id, body) => api(`/checklists/${id}`, { method: 'PUT', body }), + remove: (id) => api(`/checklists/${id}`, { method: 'DELETE' }), + duplicate: (id) => api(`/checklists/${id}/duplicate`, { method: 'POST', body: {} }), + addItem: (id, body) => api(`/checklists/${id}/items`, { method: 'POST', body }), + updateItem: (itemId, body) => api(`/checklists/items/${itemId}`, { method: 'PUT', body }), + removeItem: (itemId) => api(`/checklists/items/${itemId}`, { method: 'DELETE' }), + checkAll: (id, done) => api(`/checklists/${id}/check-all`, { method: 'POST', body: { done } }), + templates: () => api('/checklists/templates'), + template: (id) => api(`/checklists/templates/${id}`), + createTemplate: (body) => api('/checklists/templates', { method: 'POST', body }), + updateTemplate: (id, body) => api(`/checklists/templates/${id}`, { method: 'PUT', body }), + removeTemplate: (id) => api(`/checklists/templates/${id}`, { method: 'DELETE' }), +} diff --git a/src/api/ota.js b/src/api/ota.js index a6d2601..ec2214a 100644 --- a/src/api/ota.js +++ b/src/api/ota.js @@ -5,7 +5,7 @@ // 设计原则: // - Web 端只能"查 + 提示", 不能真下载应用, 避免误导 // - Tauri 端走 hotswap 插件的官方 API, 签名校验/回滚/资源替换全交给插件 -import { getApiBase, getToken } from './client.js' +import { getApiBase } from './client.js' import { APP_VERSION, BUILD_ID } from '../version.js' // 判断是否在 Tauri WebView 内 (有 __TAURI_INTERNALS__ 即 Tauri 环境) @@ -33,6 +33,47 @@ export function initOta() { .catch((e) => console.warn('[ota] notifyReady failed:', e)) } +// --------------------------------------------------------------------------- +// 判新模式 (V0.2) +// 「开发版本」开关 (我的页 OTA 组内, 默认关闭), 值持久化在设备端 localStorage: +// 开关 关闭 (默认 / 生产口径): 用**版本号**判新 —— 取服务端 manifest 的 version, +// 与当前运行版本按数字字段逐段比较大小 (0.2.0 > 0.1.0)。 +// 这样即使某个旧版本分支被重新构建 (sequence 变大但版本号更小), 也不会把新设备拉回旧版。 +// 开关 打开 (开发口径): 用 **build sequence** 判新 —— 直接走插件 checkUpdate(), +// 只要服务端 sequence 比本地大就算新, 每次提交都能推到设备上, 便于联调。 +// --------------------------------------------------------------------------- +const OTA_DEV_MODE_KEY = 'iboard_ota_dev_mode' + +export function getOtaDevMode() { + try { + return localStorage.getItem(OTA_DEV_MODE_KEY) === '1' + } catch { /* ignore */ } + return false +} + +export function setOtaDevMode(v) { + try { localStorage.setItem(OTA_DEV_MODE_KEY, v ? '1' : '0') } catch { /* ignore */ } +} + +// 版本号 → 数字字段数组 / 比较函数 (纯函数放在 utils, 便于单测) +import { compareVersion, parseVersionFields } from '../utils/version.js' + +export { compareVersion, parseVersionFields } + +// 拉服务端最新 manifest (不分平台) +// /api/ota/check/0: current_sequence 传 0, 只要有 OTA 包就一定返回 200 + manifest +export async function fetchOtaManifest() { + try { + const res = await fetch(resolveUrl('/api/ota/check/0'), { method: 'GET' }) + if (res.status === 204) return null + if (!res.ok) return null + return await res.json() + } catch (e) { + console.warn('[ota] manifest 拉取失败:', e) + return null + } +} + // 检查更新 // 返回统一结构, 调用方不关心是 Tauri 还是 web: // { @@ -40,34 +81,71 @@ export function initOta() { // reason: 'no_package' | 'same_version' | 'ota_disabled' | 'unsupported' | 'ok', // info: { version, sequence, notes, mandatory, bundle_size } // } -export async function checkOtaUpdate() { +// opts.devMode: true=按 build sequence 判新, false=按版本号判新 (默认) +export async function checkOtaUpdate(opts) { + const devMode = !!(opts && opts.devMode) if (!inTauri) { - return checkOtaUpdateWeb() + return checkOtaUpdateWeb(devMode) } - return checkOtaUpdateTauri() + return checkOtaUpdateTauri(devMode) } -async function checkOtaUpdateTauri() { - // 走 hotswap 插件 (内部会带 sequence / platform / arch 等参数) - const { checkUpdate } = await import('tauri-plugin-hotswap-api') - const r = await checkUpdate() - if (!r.available) { - return { available: false, reason: 'no_package', info: r } +// 取"当前运行版本": 已应用 OTA 覆盖包则用覆盖包版本, 否则用内置包版本 +async function currentRunningVersion() { + try { + const v = await getOtaVersionInfo() + if (v?.active && v.version) return v.version + } catch { /* ignore */ } + return APP_VERSION +} + +// 版本号口径判新 (Tauri + Web 共用): 服务端 version 比当前运行版本大才算新 +async function checkByVersion() { + const [manifest, current] = await Promise.all([fetchOtaManifest(), currentRunningVersion()]) + if (!manifest || !manifest.version) { + return { available: false, reason: 'no_package', info: null } + } + if (compareVersion(manifest.version, current) <= 0) { + return { available: false, reason: 'same_version', info: manifest } } return { available: true, reason: 'ok', info: { - version: r.version, - sequence: r.sequence, - notes: r.notes, - mandatory: r.mandatory, - bundle_size: r.bundle_size, + version: manifest.version, + sequence: manifest.sequence, + notes: manifest.notes || '', + mandatory: !!manifest.mandatory, + bundle_size: manifest.bundle_size || 0, }, } } -async function checkOtaUpdateWeb() { +async function checkOtaUpdateTauri(devMode) { + // 开发口径: sequence 判新, 直接走插件 (插件内部用当前 sequence 请求 /api/ota/check/:seq) + if (devMode) { + const { checkUpdate } = await import('tauri-plugin-hotswap-api') + const r = await checkUpdate() + if (!r.available) { + return { available: false, reason: 'no_package', info: r } + } + return { + available: true, + reason: 'ok', + info: { + version: r.version, + sequence: r.sequence, + notes: r.notes, + mandatory: r.mandatory, + bundle_size: r.bundle_size, + }, + } + } + // 生产口径: 版本号判新 + return checkByVersion() +} + +async function checkOtaUpdateWeb(devMode) { // Web 端不能下载, 只能拿到版本号/构建号做比对, 提示用户刷新 try { const res = await fetch(resolveUrl('/api/ota/manifest-web'), { method: 'GET' }) @@ -76,7 +154,11 @@ async function checkOtaUpdateWeb() { } const data = await res.json() if (!data.available) return { available: false, reason: 'no_package', info: data } - if (data.build_id && data.build_id === BUILD_ID) { + // Web 端拿不到本地 sequence: + // 开发口径 (devMode) → 只要服务端版本与本地不同就算新 (近似 sequence 语义, 含回退) + // 生产口径 → 严格比较版本号大小, 只有更高版本才算新 + const cmp = compareVersion(data.version, APP_VERSION) + if (devMode ? cmp === 0 : cmp <= 0) { return { available: false, reason: 'same_version', info: data } } return { diff --git a/src/components/BottomNav.jsx b/src/components/BottomNav.jsx index 52b1606..0479d0e 100644 --- a/src/components/BottomNav.jsx +++ b/src/components/BottomNav.jsx @@ -4,23 +4,23 @@ import TabIcon from './TabIcon.jsx' // 底部 Tab 配置: key 既是路由前缀, 又是 icon 名 // 使用 antd-mobile TabBar: 自带 safe-area 处理, 仿原生 iOS/Android 风格 +// V0.2: 打卡置最左并为默认 Tab; 原「首页」的报警/新闻并入「宏观」, 不再有首页 Tab const TABS = [ - { key: '/', name: 'home', label: '首页' }, + { key: '/checkin', name: 'checkin', label: '打卡' }, { key: '/macro', name: 'macro', label: '宏观' }, { key: '/monitor', name: 'monitor', label: '监控' }, { key: '/me', name: 'me', label: '我' }, ] -// 根据当前路径计算激活的 tab key (精确匹配 '/' , 其他前缀匹配) +// 根据当前路径计算激活的 tab key (精确匹配 + 前缀匹配) function activeKeyFromPath(pathname) { for (const t of TABS) { - if (t.key === '/') { - if (pathname === '/') return t.key - } else if (pathname === t.key || pathname.startsWith(t.key + '/')) { + if (pathname === t.key || pathname.startsWith(t.key + '/')) { return t.key } } - return '/' + // 未知路径兜底到第一个 Tab (打卡) + return TABS[0].key } // 移动端底部 Tab 导航: antd-mobile TabBar, 内置安全区 @@ -46,4 +46,4 @@ export default function BottomNav() { ))} ) -} +} diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx new file mode 100644 index 0000000..8c56b75 --- /dev/null +++ b/src/components/PageHeader.jsx @@ -0,0 +1,12 @@ +// 四个主 Tab 的统一头部 (V0.2.1 界面调整) +// - 高度为原 antd-mobile NavBar 的一半 (23px) +// - 标题小字 + 主色, 仅作定位用, 不抢内容视觉 +// - 「我」页不使用该头部 (页面顶部直接是头像区), 因此本组件只被 +// 打卡 / 宏观 / 监控 三页引用, 互不影响 +export default function PageHeader({ title }) { + return ( +
+ {title} +
+ ) +} diff --git a/src/components/RingProgress.jsx b/src/components/RingProgress.jsx new file mode 100644 index 0000000..db10872 --- /dev/null +++ b/src/components/RingProgress.jsx @@ -0,0 +1,120 @@ +import { useEffect, useRef, useState } from 'react' + +// 环形进度组件 (F-CK-03/04/05, U-01) +// props: +// percent: 0~1 完成度 +// size: 外框尺寸(px) +// stroke: 环宽(px) +// color: 进度色 +// completed: 是否达标 (达标时显示对勾而非数字) +// done/target: 中心文案 +// onClick: 可选, 点击整个环触发 (用于"点环即打卡") +export default function RingProgress({ + percent = 0, + size = 58, + stroke = 6, + color = '#1677ff', + completed = false, + done = 0, + target = 1, + over = 0, + children, + onClick, + disabled = false, +}) { + const p = Math.max(0, Math.min(1, Number(percent) || 0)) + const r = (size - stroke) / 2 + const c = 2 * Math.PI * r + const offset = c * (1 - p) + + return ( +
+ + {/* 背景环 */} + + {/* 进度环: 从 12 点方向顺时针 */} + + +
+ {children !== undefined ? children : ( + completed ? ( + + + + ) : ( + + {done} + /{target} + + ) + )} +
+ {over > 0 && +{over}} +
+ ) +} + +// 完成特效 (F-CK-07, U-02/U-03/U-07) +// props: fire 计数器(每次 +1 触发一次), color 主色 +// V0.2 设计变更: 去掉特效开关, 达标特效**恒开** (没有任何关闭入口) +export function Celebrate({ fire = 0, color = '#1677ff' }) { + const [burst, setBurst] = useState(0) + const [visible, setVisible] = useState(false) + const prev = useRef(fire) + const timer = useRef(null) + + useEffect(() => { + if (fire === prev.current) return + prev.current = fire + if (fire <= 0) return + setBurst((n) => n + 1) + setVisible(true) + haptic() + clearTimeout(timer.current) + timer.current = setTimeout(() => setVisible(false), 900) + return () => clearTimeout(timer.current) + }, [fire]) + + if (!visible) return null + const dots = Array.from({ length: 14 }, (_, i) => i) + return ( +
+ + {dots.map((i) => { + const angle = (360 / dots.length) * i + (i % 2 ? 8 : -8) + const dist = 26 + (i % 4) * 8 + return ( + + ) + })} +
+ ) +} + +// 轻量触感反馈 (Web Vibration API; Tauri Android WebView 支持) +export function haptic(ms = 18) { + try { + if (typeof navigator !== 'undefined' && navigator.vibrate) navigator.vibrate(ms) + } catch { /* ignore */ } +} diff --git a/src/components/SwipeTabs.jsx b/src/components/SwipeTabs.jsx index 5aa1b55..e885b2b 100644 --- a/src/components/SwipeTabs.jsx +++ b/src/components/SwipeTabs.jsx @@ -1,20 +1,83 @@ +import { useCallback, useEffect, useRef, useState } from 'react' import { Tabs } from 'antd-mobile' // 顶部分类 + 下方仅渲染当前激活 tab 的内容 // - 顶部: antd-mobile Tabs (点击切换) // - 下方: 同时挂载所有 tab, 通过 [hidden] 仅显示激活的那个 (保留各 tab 内部状态) +// - 右侧溢出提示: 当 tab 条还能向右滚动时, 右边缘显示渐变 + 箭头, +// 提示"右侧还有更多 tab"; 滚动到末尾后自动消失 // props: // tabs: [{ key, label, render }] // active: 当前激活 key // onChange(key): 切换回调 export default function SwipeTabs({ tabs, active, onChange }) { + const wrapRef = useRef(null) + const [more, setMore] = useState(false) + const [headerH, setHeaderH] = useState(45) + + // 判断右侧是否还有未展示的 tab (顺带记录 tab 条高度, 让提示层只覆盖页签行) + const measure = useCallback(() => { + const wrap = wrapRef.current + if (!wrap) return + const list = wrap.querySelector('.adm-tabs-tab-list') + const header = wrap.querySelector('.adm-tabs-header') + if (header && header.offsetHeight > 0) setHeaderH(header.offsetHeight) + if (!list) return + setMore(list.scrollLeft + list.clientWidth < list.scrollWidth - 2) + }, []) + + useEffect(() => { + const wrap = wrapRef.current + if (!wrap) return + const list = wrap.querySelector('.adm-tabs-tab-list') + const header = wrap.querySelector('.adm-tabs-header') + if (!list) return + + // 首次布局未稳定 (字体/宽度), 下一帧 + 300ms 各补测一次 + const raf = requestAnimationFrame(measure) + const timer = setTimeout(measure, 300) + list.addEventListener('scroll', measure, { passive: true }) + const ro = new ResizeObserver(measure) + ro.observe(list) + if (header) ro.observe(header) + return () => { + cancelAnimationFrame(raf) + clearTimeout(timer) + list.removeEventListener('scroll', measure) + ro.disconnect() + } + }, [measure, tabs.length]) + + // 切换 tab 后把激活项滚到可见区域中间, 避免选中项被藏在屏幕外 + useEffect(() => { + const wrap = wrapRef.current + if (!wrap) return + const list = wrap.querySelector('.adm-tabs-tab-list') + const activeEl = wrap.querySelector('.adm-tabs-tab-active') + if (!list || !activeEl) return + const target = activeEl.offsetLeft - (list.clientWidth - activeEl.offsetWidth) / 2 + list.scrollTo({ left: Math.max(0, target), behavior: 'smooth' }) + }, [active, tabs.length]) + return ( -
+
{tabs.map((t) => ( ))} + {more && ( +
{ + const list = wrapRef.current?.querySelector('.adm-tabs-tab-list') + if (list) list.scrollTo({ left: list.scrollLeft + list.clientWidth * 0.6, behavior: 'smooth' }) + }} + > + › +
+ )}
{tabs.map((t) => (
) -} +} diff --git a/src/components/TabIcon.jsx b/src/components/TabIcon.jsx index 51c00d1..65e4833 100644 --- a/src/components/TabIcon.jsx +++ b/src/components/TabIcon.jsx @@ -2,13 +2,7 @@ // props: name 字符串, active 布尔 // 返回对应的 24x24 SVG 节点 const ICONS = { - home: (active) => ( - - - - {active ? : null} - - ), + // V0.2: 打卡移到最左, 原「首页」已并入宏观, home 图标不再使用 macro: (active) => ( @@ -22,6 +16,15 @@ const ICONS = { ), + // 打卡: 日历 + 对勾 + checkin: (active) => ( + + + + + + + ), me: (active) => ( @@ -34,4 +37,4 @@ export default function TabIcon({ name, active }) { const render = ICONS[name] if (!render) return null return {render(!!active)} -} +} diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx deleted file mode 100644 index 387e587..0000000 --- a/src/pages/HomePage.jsx +++ /dev/null @@ -1,231 +0,0 @@ -import { useEffect, useState, useRef, useCallback } from 'react' -import { useNavigate } from 'react-router-dom' -import { NavBar, Card, Empty, DotLoading, Badge } from 'antd-mobile' -import { api } from '../api/client.js' -import SwipeTabs from '../components/SwipeTabs.jsx' -import useRefreshOnVisible from '../hooks/useRefreshOnVisible.js' - -// 首页: 报警模块 + 新闻模块 -// - 报警: World Bank 与妙想 MCP 跨源同 (指标, 周期) 数据差异超阈值 (按时间倒序) -// - 新闻: mx_finance_search_news 每日 3 点拉取国内外经济政治新闻 (按时间倒序) -// - 两个模块均无限滚动, 触底自动加载下一页 -function fmtDateTime(s) { - if (!s) return '' - const d = new Date(s) - if (isNaN(d.getTime())) return s - const pad = (n) => String(n).padStart(2, '0') - return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) -} -function fmtDate(s) { - if (!s) return '' - const d = new Date(s) - if (isNaN(d.getTime())) return s - const pad = (n) => String(n).padStart(2, '0') - return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) -} - -export default function HomePage() { - const [active, setActive] = useState('news') // 默认选中新闻 - const [alertCount, setAlertCount] = useState(0) - const handleAlertCount = useCallback((n) => setAlertCount(n), []) - - const tabs = [ - { - key: 'alerts', - label: ( - - 报警 - {alertCount > 0 && } - - ), - render: () => , - }, - { key: 'news', label: '新闻', render: () => }, - ] - - return ( -
- -
- -
-
- ) -} - -// 报警模块 -function AlertSection({ onCount }) { - const [items, setItems] = useState([]) - const [loading, setLoading] = useState(false) - const [done, setDone] = useState(false) - const cursorRef = useRef(null) - const sentinelRef = useRef(null) - const inFlightRef = useRef(false) - - const fetchPage = useCallback(async () => { - if (inFlightRef.current || done) return - inFlightRef.current = true - setLoading(true) - try { - const url = '/alerts?limit=10' + (cursorRef.current ? '&cursor=' + cursorRef.current : '') - const data = await api(url) - setItems((prev) => prev.concat(data.items || [])) - onCount?.(data.total ?? (data.items?.length || 0)) - cursorRef.current = data.nextCursor - if (!data.nextCursor) setDone(true) - } catch (e) { - console.error('alerts load error', e) - setDone(true) - } finally { - inFlightRef.current = false - setLoading(false) - } - }, [done, onCount]) - - useEffect(() => { fetchPage() }, []) - // 后台回前台: 重置游标重拉第一页, 新数据到了再更新视图 - const reload = useCallback(async () => { - if (inFlightRef.current) return - inFlightRef.current = true - setLoading(true) - try { - const data = await api('/alerts?limit=10') - setItems(data.items || []) - onCount?.(data.total ?? (data.items?.length || 0)) - cursorRef.current = data.nextCursor - setDone(!data.nextCursor) - } catch (e) { - console.error('alerts reload error', e) - setDone(true) - } finally { - inFlightRef.current = false - setLoading(false) - } - }, [onCount]) - useRefreshOnVisible(reload) - useEffect(() => { - if (done || items.length === 0) return - const el = sentinelRef.current - if (!el) return - const io = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting) fetchPage() - }, { rootMargin: '200px' }) - io.observe(el) - return () => io.disconnect() - }, [items.length, done, fetchPage]) - - return ( -
-
报警
- {items.length === 0 && !loading ? ( - - ) : ( -
- {items.map((a) => ( - -
- {a.indicatorName} - {a.period} -
-
- {a.sourceA} = {a.valueA} vs {a.sourceB} = {a.valueB} - 差异 {(a.diffRatio * 100).toFixed(1)}% -
-
{fmtDateTime(a.createdAt)}
-
- ))} -
- {loading ? : (done ? '— 已加载全部 —' : '')} -
-
- )} -
- ) -} - -// 新闻模块 -function NewsSection() { - const [items, setItems] = useState([]) - const [loading, setLoading] = useState(false) - const [done, setDone] = useState(false) - const cursorRef = useRef(null) - const sentinelRef = useRef(null) - const inFlightRef = useRef(false) - const navigate = useNavigate() - - const fetchPage = useCallback(async () => { - if (inFlightRef.current || done) return - inFlightRef.current = true - setLoading(true) - try { - const url = '/news?limit=10' + (cursorRef.current ? '&cursor=' + encodeURIComponent(cursorRef.current) : '') - const data = await api(url) - setItems((prev) => prev.concat(data.items || [])) - cursorRef.current = data.nextCursor - if (!data.nextCursor) setDone(true) - } catch (e) { - console.error('news load error', e) - setDone(true) - } finally { - inFlightRef.current = false - setLoading(false) - } - }, [done]) - - useEffect(() => { fetchPage() }, []) - // 后台回前台: 重置游标重拉第一页, 新数据到了再更新视图 - const reload = useCallback(async () => { - if (inFlightRef.current) return - inFlightRef.current = true - setLoading(true) - try { - const data = await api('/news?limit=10') - setItems(data.items || []) - cursorRef.current = data.nextCursor - setDone(!data.nextCursor) - } catch (e) { - console.error('news reload error', e) - setDone(true) - } finally { - inFlightRef.current = false - setLoading(false) - } - }, []) - useRefreshOnVisible(reload) - useEffect(() => { - if (done || items.length === 0) return - const el = sentinelRef.current - if (!el) return - const io = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting) fetchPage() - }, { rootMargin: '200px' }) - io.observe(el) - return () => io.disconnect() - }, [items.length, done, fetchPage]) - - return ( -
-
新闻
- {items.length === 0 && !loading ? ( - - ) : ( -
- {items.map((n) => ( - navigate('/news/' + n.id)}> -
{n.title}
- {n.summary &&
{n.summary}
} -
- {n.source || '未知来源'} - {fmtDate(n.publishedAt)} - #{n.impactRank} -
-
- ))} -
- {loading ? : (done ? '— 已加载全部 —' : '')} -
-
- )} -
- ) -} diff --git a/src/pages/MacroPage.jsx b/src/pages/MacroPage.jsx index 7bdc27b..9454691 100644 --- a/src/pages/MacroPage.jsx +++ b/src/pages/MacroPage.jsx @@ -1,21 +1,30 @@ import { useState } from 'react' -import { useNavigate, useLocation, Routes, Route, Navigate } from 'react-router-dom' -import { NavBar } from 'antd-mobile' +import { useNavigate, useLocation, Routes, Route } from 'react-router-dom' +import { Badge } from 'antd-mobile' +import PageHeader from '../components/PageHeader.jsx' import SwipeTabs from '../components/SwipeTabs.jsx' import DataView from './macro/DataView.jsx' import StrategyView from './macro/StrategyView.jsx' import ArticlesView from './macro/ArticlesView.jsx' import ArticleDetail from './macro/ArticleDetail.jsx' import OilPage from './macro/OilPage.jsx' +import AlertsView from './macro/AlertsView.jsx' +import NewsView from './macro/NewsView.jsx' +import NewsDetail from './news/NewsDetail.jsx' -// 宏观: 顶栏 4 tab (油价 / 经济数据 / 策略研究 / 经济文章), 默认 油价 -// /macro → 显示 oil tab (本组件 activeFromPath 决定) -// /macro/oil → 油价 (新增) +// 宏观: 顶栏 6 tab, 默认 新闻 +// /macro → 显示 news tab (activeFromPath 决定) +// /macro/alerts → 报警 (V0.2: 原「首页」tab 合并进来, 置最左) +// /macro/news → 新闻 (V0.2: 原「首页」tab 合并进来, 置第二并作为默认) +// /macro/oil → 油价 // /macro/data → 经济数据 // /macro/strategy → 策略研究 // /macro/articles → 经济文章 -// /macro/articles/:slug → 文章详情 +// 详情: /macro/data/:name · /macro/strategy/:name · /macro/articles/:slug · /macro/news/:id +// 说明: tab 数量已超出屏宽, SwipeTabs 会在右侧显示渐变 + 箭头提示可继续滑动 const TABS = [ + { key: 'alerts', label: '报警', path: '/macro/alerts' }, + { key: 'news', label: '新闻', path: '/macro/news' }, { key: 'oil', label: '油价', path: '/macro/oil' }, { key: 'data', label: '经济数据', path: '/macro/data' }, { key: 'strategy', label: '策略研究', path: '/macro/strategy' }, @@ -23,27 +32,35 @@ const TABS = [ ] function activeFromPath(pathname) { + if (pathname.startsWith('/macro/alerts')) return 'alerts' + if (pathname.startsWith('/macro/news')) return 'news' if (pathname.startsWith('/macro/oil')) return 'oil' - if (pathname.startsWith('/macro/articles')) return 'articles' - if (pathname.startsWith('/macro/strategy')) return 'strategy' if (pathname.startsWith('/macro/data')) return 'data' - // 父路由 /macro 或 / 走 Tab 列表第一个 (油价) - return 'oil' + if (pathname.startsWith('/macro/strategy')) return 'strategy' + if (pathname.startsWith('/macro/articles')) return 'articles' + // 父路由 /macro 走默认 tab (新闻) + return 'news' } export default function MacroPage() { const location = useLocation() const navigate = useNavigate() const active = activeFromPath(location.pathname) + // 报警角标: 由 AlertsView 回传总数 + const [alertCount, setAlertCount] = useState(0) const tabs = TABS.map((t) => ({ key: t.key, - label: t.label, + label: t.key === 'alerts' + ? 报警{alertCount > 0 && } + : t.label, render: () => { if (t.key === 'oil') return if (t.key === 'data') return if (t.key === 'strategy') return if (t.key === 'articles') return + if (t.key === 'alerts') return + if (t.key === 'news') return return null }, })) @@ -53,20 +70,21 @@ export default function MacroPage() { if (next) navigate(next.path) } - // 详情页 /macro/articles/:slug 和 /macro/data/:name 单独走 React Router 渲染, 不参与滑屏 - if (location.pathname.match(/^\/macro\/(articles|data|strategy)\/[^/]+$/)) { + // 详情页单独走 React Router 渲染, 不参与滑屏 + if (location.pathname.match(/^\/macro\/(articles|data|strategy|news)\/[^/]+$/)) { return ( } /> } /> } /> + } /> ) } return (
- +
) diff --git a/src/pages/MePage.jsx b/src/pages/MePage.jsx index b393c50..d98f81e 100644 --- a/src/pages/MePage.jsx +++ b/src/pages/MePage.jsx @@ -1,15 +1,17 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { Avatar, Button, List, NavBar, ProgressBar, Toast } from 'antd-mobile' +import { Avatar, Button, List, ProgressBar, Switch, Toast } from 'antd-mobile' import { useAuth } from '../auth/AuthContext.jsx' import { APP_VERSION, BUILD_ID } from '../version.js' import { applyOtaUpdate, checkOtaUpdate, getCurrentVersionInfo, + getOtaDevMode, getOtaVersionInfo, reloadApp, rollbackOta, + setOtaDevMode, } from '../api/ota.js' // "我" 页: 顶部 NavBar + 用户头像卡片 + 信息列表 + OTA 升级区 @@ -29,6 +31,8 @@ export default function MePage() { const [otaVersion, setOtaVersion] = useState(null) // hotswap 当前激活的覆盖包版本 const [progress, setProgress] = useState({ received: 0, total: 0 }) const [error, setError] = useState('') + // 判新模式: false(默认)=版本号比较, true=build sequence 比较; 值持久化在设备端 + const [devMode, setDevMode] = useState(() => getOtaDevMode()) // 进入页面时拉一次 hotswap 状态 (Tauri 内才需要, web 下 null 也无所谓) useEffect(() => { @@ -43,7 +47,7 @@ export default function MePage() { setError('') setOtaState('checking') try { - const r = await checkOtaUpdate() + const r = await checkOtaUpdate({ devMode }) if (r.available) { setOtaInfo(r.info) setOtaState('available') @@ -111,7 +115,6 @@ export default function MePage() { return (
-
运行资源 + {/* 判新口径开关: 默认关(版本号比较), 打开后按 build sequence 比较 */} + { + setDevMode(v) + setOtaDevMode(v) + // 切换口径后旧检查结果失效, 重置避免误导 + setOtaState('idle') + setOtaInfo(null) + setError('') + Toast.show({ content: v ? '开发版本: 按构建号判新' : '正式口径: 按版本号判新' }) + }} + /> + } + description={ + devMode + ? '按 build sequence 判新:每次构建都算新版本,便于开发联调' + : '按版本号判新:仅当服务端版本号更高时才提示更新' + } + > + 开发版本 +
) -} +} diff --git a/src/pages/MonitorPage.jsx b/src/pages/MonitorPage.jsx index 6dee73a..04c2635 100644 --- a/src/pages/MonitorPage.jsx +++ b/src/pages/MonitorPage.jsx @@ -1,7 +1,8 @@ import { useEffect, useState, useCallback } from 'react' import { useNavigate } from 'react-router-dom' -import { NavBar, Card, Empty, DotLoading } from 'antd-mobile' +import { Card, Empty, DotLoading } from 'antd-mobile' import { api } from '../api/client.js' +import PageHeader from '../components/PageHeader.jsx' import SwipeTabs from '../components/SwipeTabs.jsx' import useRefreshOnVisible from '../hooks/useRefreshOnVisible.js' import EnvView from './monitor/EnvView.jsx' @@ -46,7 +47,7 @@ export default function MonitorPage() { ] return (
- +
diff --git a/src/pages/checkin/CalendarView.jsx b/src/pages/checkin/CalendarView.jsx new file mode 100644 index 0000000..3196631 --- /dev/null +++ b/src/pages/checkin/CalendarView.jsx @@ -0,0 +1,173 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { Button, DotLoading, Empty, Segmented, Toast } from 'antd-mobile' +import { Celebrate } from '../../components/RingProgress.jsx' +import { checkinApi } from '../../api/checkin.js' +import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' +import { + toDateStr, addDays, addMonths, monthGrid, weekdayLabels, dayLabel, fmtMonth, +} from './date.js' + +// 日历视图: 月历网格 + 今天/昨天/明天 三视图 + 日期标记 (F-CL-*) +export default function CalendarView({ date, onDateChange }) { + const today = toDateStr() + const [month, setMonth] = useState(() => date.slice(0, 7)) + const [cal, setCal] = useState(null) // 月历数据 + const [detail, setDetail] = useState(null) // 选中日详情 + const [err, setErr] = useState('') + const [bursts, setBursts] = useState({}) + + const loadCal = useCallback((silent) => { + if (!silent) setErr('') + checkinApi.calendar(month) + .then(setCal) + .catch((e) => { if (!silent) setErr(e.message) }) + }, [month]) + + const loadDetail = useCallback((silent) => { + checkinApi.day(date).then(setDetail).catch(() => {}) + }, [date]) + + useEffect(() => { loadCal(false) }, [loadCal]) + useEffect(() => { setDetail(null); loadDetail(false) }, [loadDetail]) + useRefreshOnVisible(() => { loadCal(true); loadDetail(true) }) + + // 切换日期时, 若跨月则同步月份 + useEffect(() => { + const m = date.slice(0, 7) + setMonth((cur) => (cur === m ? cur : m)) + }, [date]) + + const days = useMemo(() => monthGrid(month), [month]) + const byDate = useMemo(() => { + const map = new Map() + ;(cal?.days || []).forEach((d) => map.set(d.date, d)) + return map + }, [cal]) + + const quickValue = useMemo(() => { + if (date === today) return 0 + if (date === addDays(today, -1)) return -1 + if (date === addDays(today, 1)) return 1 + return null + }, [date, today]) + + async function checkFromCalendar(item) { + if (!item.canCheck) return + const snapshot = detail + setDetail((d) => ({ + ...d, + items: d.items.map((x) => { + if (x.id !== item.id) return x + const done = x.done + 1 + const countOnDate = (x.countOnDate || 0) + 1 + return { + ...x, done, countOnDate, + percent: Math.min(1, done / x.target), + completed: done >= x.target, + over: Math.max(0, done - x.target), + dayDone: countOnDate >= x.expectedPerDay, + } + }), + })) + try { + const res = await checkinApi.check(item.id, date) + const fresh = await checkinApi.day(date) + setDetail(fresh) + if (res.item.completed) setBursts((m) => ({ ...m, [item.id]: (m[item.id] || 0) + 1 })) + loadCal(true) + } catch (e) { + setDetail(snapshot) + Toast.show({ content: e.message || '打卡失败' }) + } + } + + const items = detail?.items || [] + const isFuture = !!detail?.isFuture + + return ( +
+
+ setMonth((m) => addMonths(m, -1))}>‹ + {fmtMonth(month)} + setMonth((m) => addMonths(m, 1))}>› + onDateChange(today)}>回到今天 +
+ +
+ onDateChange(addDays(today, Number(v)))} + /> +
+ + {err &&
{err} loadCal(false)}>重试
} + +
+
+ {weekdayLabels().map((w) => {w})} +
+
+ {days.map((d) => { + const info = byDate.get(d) + const inMonth = d.slice(0, 7) === month + const status = info?.status || 'empty' + return ( +
onDateChange(d)} + > + {Number(d.slice(8, 10))} + {inMonth && status !== 'empty' && ( + + )} +
+ ) + })} +
+
+ + {cal && ( +
本月完成率 {Math.round((cal.rate || 0) * 100)}%
+ )} + +
+
+ {dayLabel(date, today)} · {date} + {isFuture && 计划(不可打卡)} +
+ + {detail === null &&
加载中...
} + {detail !== null && items.length === 0 && } + + {items.map((item) => ( +
+ + {item.name} + + {item.periodLabel} {item.target} 次 · 当日 {item.countOnDate || 0}/{item.expectedPerDay} + + + {item.canCheck ? ( + + ) : ( + {item.dayDone ? '计划' : '待办'} + )} + + +
+ ))} +
+
+ ) +} diff --git a/src/pages/checkin/CheckinPage.jsx b/src/pages/checkin/CheckinPage.jsx new file mode 100644 index 0000000..8287c17 --- /dev/null +++ b/src/pages/checkin/CheckinPage.jsx @@ -0,0 +1,29 @@ +import { useState } from 'react' +import PageHeader from '../../components/PageHeader.jsx' +import SwipeTabs from '../../components/SwipeTabs.jsx' +import HabitView from './HabitView.jsx' +import CalendarView from './CalendarView.jsx' +import ListView from './ListView.jsx' +import { toDateStr } from './date.js' + +// 打卡页: 三个视图 —— 打卡(环形进度) / 日历(今天·昨天·明天) / 清单(检查清单 + 模板) +// 注: 打卡与日历的合并视图正在设计确认中, 确认后此处收敛为 日历(含打卡) / 清单 两个视图 +export default function CheckinPage() { + const [active, setActive] = useState('habit') + const [date, setDate] = useState(() => toDateStr()) + + const tabs = [ + { key: 'habit', label: '打卡', render: () => }, + { key: 'calendar', label: '日历', render: () => }, + { key: 'list', label: '清单', render: () => }, + ] + + return ( +
+ +
+ +
+
+ ) +} diff --git a/src/pages/checkin/HabitView.jsx b/src/pages/checkin/HabitView.jsx new file mode 100644 index 0000000..cc9ca08 --- /dev/null +++ b/src/pages/checkin/HabitView.jsx @@ -0,0 +1,282 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { + Button, Dialog, DotLoading, Empty, Input, Popup, Selector, Stepper, Toast, +} from 'antd-mobile' +import RingProgress, { Celebrate } from '../../components/RingProgress.jsx' +import { checkinApi } from '../../api/checkin.js' +import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' +import { toDateStr, dayLabel } from './date.js' + +const PERIOD_OPTIONS = [ + { label: '每天', value: 'day' }, + { label: '每周', value: 'week' }, + { label: '每月', value: 'month' }, + { label: '每年', value: 'year' }, +] + +const COLORS = ['#1677ff', '#00b578', '#ff8f1f', '#ff3141', '#a855f7', '#14b8a6', '#f43f9e'] + +const EMPTY_FORM = { id: null, name: '', period: 'day', target: 1, color: COLORS[0], note: '' } + +// 打卡视图: 今日待办列表 + 环形进度 + 打卡/撤销 + 新建/编辑 +export default function HabitView({ date, onDateChange }) { + const [data, setData] = useState(null) // { today, date, items, summary } + const [err, setErr] = useState('') + const [bursts, setBursts] = useState({}) // { habitId: 触发计数 } + const [form, setForm] = useState(EMPTY_FORM) + const [showForm, setShowForm] = useState(false) + + const load = useCallback((silent) => { + if (!silent) setErr('') + checkinApi.habits(date) + .then((d) => setData(d)) + .catch((e) => { if (!silent) setErr(e.message) }) + }, [date]) + + useEffect(() => { load(false) }, [load]) + useRefreshOnVisible(() => load(true)) + + const items = useMemo(() => { + const list = data?.items || [] + // 未完成在前, 已完成在后 (F-CK-16 默认排序) + return [...list].sort((a, b) => Number(a.completed) - Number(b.completed) || a.id - b.id) + }, [data]) + + const fireBurst = (id) => setBursts((m) => ({ ...m, [id]: (m[id] || 0) + 1 })) + + // 乐观更新: 先本地 +1, 成功用服务端结果覆盖, 失败回滚 (F-SY-06/E-02) + async function doCheck(item) { + const before = data + const patch = (it) => { + const done = it.done + 1 + return { + ...it, + done, + remaining: Math.max(0, it.target - done), + percent: Math.min(1, done / it.target), + completed: done >= it.target, + over: Math.max(0, done - it.target), + countOnDate: (it.countOnDate || 0) + 1, + dayDone: (it.countOnDate || 0) + 1 >= it.expectedPerDay, + } + } + setData((d) => ({ ...d, items: d.items.map((x) => (x.id === item.id ? patch(x) : x)) })) + try { + const res = await checkinApi.check(item.id, date) + setData((d) => ({ ...d, items: d.items.map((x) => (x.id === item.id ? res.item : x)) })) + if (res.item.completed) fireBurst(item.id) + } catch (e) { + setData(before) + Toast.show({ content: e.message || '打卡失败' }) + } + } + + async function doUncheck(item) { + const before = data + setData((d) => ({ + ...d, + items: d.items.map((x) => { + if (x.id !== item.id) return x + const done = Math.max(0, x.done - 1) + return { + ...x, + done, + remaining: Math.max(0, x.target - done), + percent: Math.min(1, done / x.target), + completed: done >= x.target, + over: Math.max(0, done - x.target), + countOnDate: Math.max(0, (x.countOnDate || 0) - 1), + } + }), + })) + try { + const res = await checkinApi.uncheck(item.id, date) + setData((d) => ({ ...d, items: d.items.map((x) => (x.id === item.id ? res.item : x)) })) + } catch (e) { + setData(before) + Toast.show({ content: e.message || '撤销失败' }) + } + } + + async function submitForm() { + const name = String(form.name || '').trim() + if (!name) return Toast.show({ content: '请填写名称' }) + if (name.length > 20) return Toast.show({ content: '名称不能超过 20 个字' }) + const payload = { name, period: form.period, target: Number(form.target) || 1, color: form.color, note: form.note } + try { + if (form.id) await checkinApi.updateHabit(form.id, payload) + else await checkinApi.createHabit(payload) + setShowForm(false) + setForm(EMPTY_FORM) + load(true) + } catch (e) { + Toast.show({ content: e.message || '保存失败' }) + } + } + + async function removeHabit(item) { + const ok = await Dialog.confirm({ + content: `归档「${item.name}」?归档后不再出现在待办,历史记录保留。`, + confirmText: '归档', + cancelText: '取消', + }) + if (!ok) return + try { + await checkinApi.deleteHabit(item.id) + Toast.show({ content: '已归档' }) + load(true) + } catch (e) { + Toast.show({ content: e.message || '操作失败' }) + } + } + + const summary = data?.summary || { total: 0, done: 0 } + const isToday = data ? data.date === data.today : true + + return ( +
+
+
+
{dayLabel(date, data?.today || toDateStr())}
+
+ 已完成 {summary.done} / {summary.total} + {!isToday && 非今天} +
+
+
+ +
+
+ + {err && ( +
+ {err} + load(false)}>重试 +
+ )} + + {data === null && !err &&
加载中...
} + + {data !== null && !err && items.length === 0 && ( +
+ + +
+ )} + +
+ {items.map((item) => ( +
+ doCheck(item)} + /> +
+
{item.name}
+
+ {item.periodLabel} {item.target} 次 + {item.streak > 0 && 连续 {item.streak}} +
+ {item.note &&
{item.note}
} +
+ {item.periodStart === item.periodEnd + ? '今日' + : `${item.periodStart.slice(5)} ~ ${item.periodEnd.slice(5)}`} + {item.over > 0 && 超额 {item.over}} +
+
+
+ +
+
+ {item.done > 0 && ( + doUncheck(item)}>撤销 + )} + { + setForm({ id: item.id, name: item.name, period: item.period, target: item.target, color: item.color, note: item.note }) + setShowForm(true) + }} + >编辑 + removeHabit(item)}>归档 +
+ +
+ ))} +
+ + {/* 新建 / 编辑 */} + setShowForm(false)} + bodyStyle={{ borderTopLeftRadius: 12, borderTopRightRadius: 12, paddingBottom: 16 }} + > +
+
{form.id ? '编辑打卡项' : '新建打卡项'}
+
+ + setForm((f) => ({ ...f, name: v }))} + /> +
+
+ + setForm((f) => ({ ...f, period: v[0] }))} + /> +
+
+ + setForm((f) => ({ ...f, target: v }))} /> +
+
+ +
+ {COLORS.map((c) => ( + setForm((f) => ({ ...f, color: c }))} + /> + ))} +
+
+
+ + setForm((f) => ({ ...f, note: v }))} + /> +
+
+ + +
+
+
+
+ ) +} diff --git a/src/pages/checkin/ListView.jsx b/src/pages/checkin/ListView.jsx new file mode 100644 index 0000000..2e8aa48 --- /dev/null +++ b/src/pages/checkin/ListView.jsx @@ -0,0 +1,528 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { + Button, Dialog, DotLoading, Empty, Input, Popup, Segmented, Toast, +} from 'antd-mobile' +import { Celebrate } from '../../components/RingProgress.jsx' +import { checklistApi } from '../../api/checkin.js' +import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' + +// 清单视图: 进行中 / 已归档 / 模板 三类并列 (F-LS-*) +// - 进行中 / 已归档: 清单本体, 勾选即完成 +// - 模板: 独立 CRUD (新建 / 编辑 / 删除), 按名称字母顺序排序 (中文按拼音) +// - 新建清单: 只填名称, 模板从右侧滑出面板中选择 (不再使用场景选择器) +const NAME_COLLATOR = new Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' }) +const byName = (a, b) => NAME_COLLATOR.compare(String(a.name || a.title || ''), String(b.name || b.title || '')) + +export default function ListView() { + const [tab, setTab] = useState('active') // active | archived | template + const [lists, setLists] = useState(null) + const [err, setErr] = useState('') + const [openId, setOpenId] = useState(null) + const [newItem, setNewItem] = useState({}) + const [bursts, setBursts] = useState({}) + const [templates, setTemplates] = useState(null) + const [showForm, setShowForm] = useState(false) + const [form, setForm] = useState({ title: '', templateId: '' }) + const [pickerOpen, setPickerOpen] = useState(false) // 右侧模板选择面板 + const [tplEdit, setTplEdit] = useState(null) // { id, name, items[] } | null + const [tplSaving, setTplSaving] = useState(false) + + const archived = tab === 'archived' + + const load = useCallback((silent) => { + if (!silent) setErr('') + checklistApi.lists(archived) + .then((d) => setLists(d.items || [])) + .catch((e) => { if (!silent) setErr(e.message) }) + }, [archived]) + + const loadTemplates = useCallback(() => { + checklistApi.templates() + .then((d) => setTemplates((d.items || []).slice().sort(byName))) + .catch(() => setTemplates([])) + }, []) + + useEffect(() => { setLists(null); load(false) }, [load]) + useRefreshOnVisible(() => { load(true); if (tab === 'template') loadTemplates() }) + useEffect(() => { loadTemplates() }, [loadTemplates]) + + const withProgress = useCallback((list) => { + const items = list.items || [] + const done = items.filter((i) => i.done).length + return { + ...list, + total: items.length, + done, + percent: items.length > 0 ? done / items.length : 0, + completed: items.length > 0 && done === items.length, + } + }, []) + + const patchList = (id, updater) => + setLists((prev) => (prev || []).map((l) => (l.id === id ? updater(l) : l))) + + // 勾选: 乐观更新 + 服务端确认 (F-LS-03, F-SY-06) + async function toggleItem(listId, item) { + const snapshot = lists + const next = !item.done + patchList(listId, (l) => withProgress({ + ...l, + items: l.items.map((i) => (i.id === item.id ? { ...i, done: next } : i)), + })) + try { + await checklistApi.updateItem(item.id, { done: next }) + const l = (lists || []).find((x) => x.id === listId) + const updated = withProgress({ + ...l, + items: l.items.map((i) => (i.id === item.id ? { ...i, done: next } : i)), + }) + if (updated.completed) setBursts((m) => ({ ...m, [listId]: (m[listId] || 0) + 1 })) + } catch (e) { + setLists(snapshot) + Toast.show({ content: e.message || '更新失败' }) + } + } + + async function addItem(listId, title) { + const t = String(title || '').trim() + if (!t) return + try { + const { item } = await checklistApi.addItem(listId, { title: t }) + patchList(listId, (l) => withProgress({ ...l, items: [...l.items, item] })) + setNewItem((m) => ({ ...m, [listId]: '' })) + } catch (e) { + Toast.show({ content: e.message || '添加失败' }) + } + } + + async function removeItem(listId, itemId) { + const snapshot = lists + patchList(listId, (l) => withProgress({ ...l, items: l.items.filter((i) => i.id !== itemId) })) + try { + await checklistApi.removeItem(itemId) + } catch (e) { + setLists(snapshot) + Toast.show({ content: e.message || '删除失败' }) + } + } + + async function checkAll(listId, done) { + const snapshot = lists + patchList(listId, (l) => withProgress({ ...l, items: l.items.map((i) => ({ ...i, done })) })) + try { + const { item } = await checklistApi.checkAll(listId, done) + patchList(listId, () => withProgress(item)) + if (item.completed) setBursts((m) => ({ ...m, [listId]: (m[listId] || 0) + 1 })) + } catch (e) { + setLists(snapshot) + Toast.show({ content: e.message || '操作失败' }) + } + } + + // ---------- 清单 ---------- + + function openCreateForm(templateId = '') { + setForm({ title: '', templateId }) + setShowForm(true) + } + + async function createList() { + const title = String(form.title || '').trim() + if (!title) return Toast.show({ content: '请填写清单名称' }) + const tpl = templateById(form.templateId) + try { + await checklistApi.create({ + title, + // 场景不再由用户选择: 沿用模板自带的场景 (无模板则为空), 兼容历史数据展示 + scene: tpl?.scene || '', + templateId: form.templateId || undefined, + }) + setShowForm(false) + setForm({ title: '', templateId: '' }) + setTab('active') + load(true) + } catch (e) { + Toast.show({ content: e.message || '创建失败' }) + } + } + + async function duplicate(list) { + try { + await checklistApi.duplicate(list.id) + Toast.show({ content: '已复制为新清单' }) + load(true) + } catch (e) { + Toast.show({ content: e.message || '复制失败' }) + } + } + + async function saveAsTemplate(list) { + const items = (list.items || []).map((i) => ({ title: i.title, note: i.note })) + if (!items.length) return Toast.show({ content: '空清单无法存为模板' }) + try { + await checklistApi.createTemplate({ name: list.title, scene: list.scene || '', items }) + Toast.show({ content: '已保存为模板' }) + loadTemplates() + } catch (e) { + Toast.show({ content: e.message || '保存失败' }) + } + } + + async function toggleArchive(list) { + try { + await checklistApi.update(list.id, { archived: !list.archived }) + Toast.show({ content: list.archived ? '已恢复到进行中' : '已归档' }) + load(true) + } catch (e) { + Toast.show({ content: e.message || '操作失败' }) + } + } + + async function removeList(list) { + const ok = await Dialog.confirm({ content: `删除「${list.title}」?`, confirmText: '删除', cancelText: '取消' }) + if (!ok) return + try { + await checklistApi.remove(list.id) + load(true) + } catch (e) { + Toast.show({ content: e.message || '删除失败' }) + } + } + + // ---------- 模板 CRUD ---------- + + function templateById(id) { + if (!id) return null + return (templates || []).find((t) => String(t.id) === String(id)) || null + } + + function newTemplate() { + setTplEdit({ id: null, name: '', items: [{ title: '' }], builtin: false }) + } + + function editTemplate(tpl) { + if (tpl.builtin) return Toast.show({ content: '系统预置模板不可编辑' }) + setTplEdit({ + id: tpl.id, + name: tpl.name, + builtin: false, + items: (Array.isArray(tpl.items) ? tpl.items : []).map((i) => ({ + title: String(i?.title || ''), + note: String(i?.note || ''), + })), + }) + } + + function setTplItem(idx, title) { + setTplEdit((t) => ({ ...t, items: t.items.map((it, i) => (i === idx ? { ...it, title } : it)) })) + } + + function addTplItem() { + setTplEdit((t) => ({ ...t, items: [...t.items, { title: '' }] })) + } + + function removeTplItem(idx) { + setTplEdit((t) => ({ ...t, items: t.items.filter((_, i) => i !== idx) })) + } + + async function saveTemplate() { + if (!tplEdit) return + const name = String(tplEdit.name || '').trim() + const items = tplEdit.items + .map((i) => ({ title: String(i.title || '').trim(), note: String(i.note || '') })) + .filter((i) => i.title) + if (!name) return Toast.show({ content: '请填写模板名称' }) + if (name.length > 20) return Toast.show({ content: '模板名称不能超过 20 个字' }) + if (!items.length) return Toast.show({ content: '模板至少需要一个检查项' }) + setTplSaving(true) + try { + if (tplEdit.id) await checklistApi.updateTemplate(tplEdit.id, { name, items }) + else await checklistApi.createTemplate({ name, items }) + Toast.show({ content: tplEdit.id ? '已保存' : '模板已创建' }) + setTplEdit(null) + loadTemplates() + } catch (e) { + Toast.show({ content: e.message || '保存失败' }) + } finally { + setTplSaving(false) + } + } + + async function removeTemplate(tpl) { + if (tpl.builtin) return Toast.show({ content: '系统预置模板不可删除' }) + const ok = await Dialog.confirm({ + content: `删除模板「${tpl.name}」?已创建的清单不受影响。`, + confirmText: '删除', + cancelText: '取消', + }) + if (!ok) return + try { + await checklistApi.removeTemplate(tpl.id) + Toast.show({ content: '模板已删除' }) + loadTemplates() + } catch (e) { + Toast.show({ content: e.message || '删除失败' }) + } + } + + const sorted = useMemo(() => lists || [], [lists]) + const templateCount = (t) => (Array.isArray(t.items) ? t.items.length : 0) + const selectedTpl = templateById(form.templateId) + + return ( +
+
+ setTab(v)} + /> + {tab === 'template' + ? + : } +
+ + {/* ---------- 模板分类 ---------- */} + {tab === 'template' && ( + <> + {templates === null &&
加载中...
} + {templates !== null && templates.length === 0 && ( + + + + )} +
+ {(templates || []).map((tpl) => ( +
+
+ {tpl.name} + {tpl.builtin && 预置} + {templateCount(tpl)} 项 +
+
+ {(Array.isArray(tpl.items) ? tpl.items : []).slice(0, 4).map((it, i) => ( + {it.title} + ))} + {templateCount(tpl) > 4 && ( + +{templateCount(tpl) - 4} + )} +
+
+ openCreateForm(String(tpl.id))}>由此新建 + {!tpl.builtin && editTemplate(tpl)}>编辑} + {!tpl.builtin && removeTemplate(tpl)}>删除} +
+
+ ))} +
+ + )} + + {/* ---------- 清单列表 ---------- */} + {tab !== 'template' && ( + <> + {err &&
{err} load(false)}>重试
} + {lists === null && !err &&
加载中...
} + {lists !== null && !err && sorted.length === 0 && ( + + {!archived && } + + )} + +
+ {sorted.map((list) => { + const open = openId === list.id + return ( +
+
setOpenId(open ? null : list.id)}> +
+ {list.title} + {list.scene && {list.scene}} +
+
+
+
+
+ {list.done}/{list.total} + › +
+
+ + {open && ( +
+ {(list.items || []).map((item) => ( +
+ { e.stopPropagation(); toggleItem(list.id, item) }} + > + + + + + toggleItem(list.id, item)}> + {item.title} + {item.note && {item.note}} + + removeItem(list.id, item.id)}>× +
+ ))} + +
+ setNewItem((m) => ({ ...m, [list.id]: v }))} + onEnterPress={() => addItem(list.id, newItem[list.id])} + /> + +
+ +
+ checkAll(list.id, true)}>全选 + checkAll(list.id, false)}>全不选 + duplicate(list)}>复制 + saveAsTemplate(list)}>存为模板 + toggleArchive(list)}>{list.archived ? '恢复' : '归档'} + removeList(list)}>删除 +
+
+ )} + +
+ ) + })} +
+ + )} + + {/* ---------- 新建清单 (名称 + 模板, 无场景) ---------- */} + setShowForm(false)} + bodyStyle={{ borderTopLeftRadius: 12, borderTopRightRadius: 12, paddingBottom: 16 }} + > +
+
新建检查清单
+
+ + setForm((f) => ({ ...f, title: v }))} + /> +
+
+ +
setPickerOpen(true)}> + + {selectedTpl ? selectedTpl.name : '不使用模板(空白清单)'} + + + {selectedTpl ? `${templateCount(selectedTpl)} 项` : ''} + › + +
+
+
+ + +
+
+
+ + {/* ---------- 模板选择: 右侧滑出面板 ---------- */} + setPickerOpen(false)} + bodyStyle={{ width: '78vw', maxWidth: 340, height: '100%', display: 'flex', flexDirection: 'column' }} + > +
+
+ 选择模板 + setPickerOpen(false)}>× +
+
按名称字母顺序排列
+
+
{ setForm((f) => ({ ...f, templateId: '' })); setPickerOpen(false) }} + > + 不使用模板 + 空白清单 +
+ {(templates || []).map((tpl) => ( +
{ setForm((f) => ({ ...f, templateId: String(tpl.id) })); setPickerOpen(false) }} + > + + {tpl.name} + {tpl.builtin && 预置} + + {templateCount(tpl)} 项 +
+ ))} + {templates !== null && templates.length === 0 && ( +
还没有可用模板
+ )} +
+
+
+ + {/* ---------- 模板编辑 ---------- */} + setTplEdit(null)} + bodyStyle={{ borderTopLeftRadius: 12, borderTopRightRadius: 12, paddingBottom: 16, maxHeight: '82vh', overflowY: 'auto' }} + > + {tplEdit && ( +
+
{tplEdit.id ? '编辑模板' : '新建模板'}
+
+ + setTplEdit((t) => ({ ...t, name: v }))} + /> +
+
+ +
+ {tplEdit.items.map((it, idx) => ( +
+ {idx + 1} + setTplItem(idx, v)} + /> + removeTplItem(idx)}>× +
+ ))} + +
+
+
+ + +
+
+ )} +
+
+ ) +} diff --git a/src/pages/checkin/date.js b/src/pages/checkin/date.js new file mode 100644 index 0000000..7881d01 --- /dev/null +++ b/src/pages/checkin/date.js @@ -0,0 +1,88 @@ +// 前端日期/周期工具: 与 server/checkin/period.js 保持同一套口径 (Asia/Shanghai) +export const TZ = 'Asia/Shanghai' + +const FMT = new Intl.DateTimeFormat('en-CA', { + timeZone: TZ, year: 'numeric', month: '2-digit', day: '2-digit', +}) + +/** 任意时间 -> 本地日历日 "YYYY-MM-DD" */ +export function toDateStr(d = new Date()) { + return FMT.format(d instanceof Date ? d : new Date(d)) +} + +function toUTC(s) { + const [y, m, d] = String(s).split('-').map(Number) + return new Date(Date.UTC(y, (m || 1) - 1, d || 1)) +} + +function fromUTC(dt) { + return dt.toISOString().slice(0, 10) +} + +export function addDays(s, n) { + const d = toUTC(s) + d.setUTCDate(d.getUTCDate() + n) + return fromUTC(d) +} + +export function addMonths(s, n) { + const [y, m] = String(s).split('-').map(Number) + const first = new Date(Date.UTC(y, (m || 1) - 1 + n, 1)) + const day = Number(String(s).slice(8, 10)) || 1 + const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate() + first.setUTCDate(Math.min(day, lastDay)) + return fromUTC(first) +} + +/** 周一为一周第一天 */ +export function startOfWeek(s) { + const dow = (toUTC(s).getUTCDay() + 6) % 7 + return addDays(s, -dow) +} + +export const PERIOD_LABEL = { day: '每天', week: '每周', month: '每月', year: '每年' } + +export function periodRange(period, s) { + switch (period) { + case 'week': { + const start = startOfWeek(s) + return { start, end: addDays(start, 6) } + } + case 'month': { + const start = String(s).slice(0, 8) + '01' + const [y, m] = String(s).split('-').map(Number) + return { start, end: fromUTC(new Date(Date.UTC(y, m || 1, 0))) } + } + case 'year': { + const y = String(s).slice(0, 4) + return { start: y + '-01-01', end: y + '-12-31' } + } + default: return { start: s, end: s } + } +} + +/** 月历网格: 返回 6*7 = 42 天 (含前后月的补位), 周一起始 */ +export function monthGrid(monthStr) { + const first = monthStr + '-01' + const gridStart = startOfWeek(first) + const days = [] + for (let i = 0; i < 42; i++) days.push(addDays(gridStart, i)) + return days +} + +export function weekdayLabels() { + return ['一', '二', '三', '四', '五', '六', '日'] +} + +/** 相对今天的文案: 昨天 / 今天 / 明天 / MM-DD */ +export function dayLabel(dateStr, today = toDateStr()) { + if (dateStr === today) return '今天' + if (dateStr === addDays(today, -1)) return '昨天' + if (dateStr === addDays(today, 1)) return '明天' + return dateStr.slice(5).replace('-', '/') +} + +export function fmtMonth(monthStr) { + const [y, m] = monthStr.split('-') + return y + '年' + Number(m) + '月' +} diff --git a/src/pages/macro/AlertsView.jsx b/src/pages/macro/AlertsView.jsx new file mode 100644 index 0000000..12bca59 --- /dev/null +++ b/src/pages/macro/AlertsView.jsx @@ -0,0 +1,101 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import { Card, DotLoading, Empty } from 'antd-mobile' +import { api } from '../../api/client.js' +import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' +import { fmtDateTime } from '../../utils/format.js' + +// 报警 (V0.2 从「首页」并入「宏观」Tab) +// - 数据: World Bank 与妙想 MCP 跨源同 (指标, 周期) 数据差异超阈值, 按时间倒序 +// - 无限滚动: 触底自动加载下一页 +// - onCount: 把总数回传给父级 (宏观 Tab 上显示角标) +export default function AlertsView({ onCount }) { + const [items, setItems] = useState([]) + const [loading, setLoading] = useState(false) + const [done, setDone] = useState(false) + const cursorRef = useRef(null) + const sentinelRef = useRef(null) + const inFlightRef = useRef(false) + + const fetchPage = useCallback(async () => { + if (inFlightRef.current || done) return + inFlightRef.current = true + setLoading(true) + try { + const url = '/alerts?limit=10' + (cursorRef.current ? '&cursor=' + cursorRef.current : '') + const data = await api(url) + setItems((prev) => prev.concat(data.items || [])) + onCount?.(data.total ?? (data.items?.length || 0)) + cursorRef.current = data.nextCursor + if (!data.nextCursor) setDone(true) + } catch (e) { + console.error('alerts load error', e) + setDone(true) + } finally { + inFlightRef.current = false + setLoading(false) + } + }, [done, onCount]) + + useEffect(() => { fetchPage() }, []) + // 后台回前台: 重置游标重拉第一页, 新数据到了再更新视图 + const reload = useCallback(async () => { + if (inFlightRef.current) return + inFlightRef.current = true + setLoading(true) + try { + const data = await api('/alerts?limit=10') + setItems(data.items || []) + onCount?.(data.total ?? (data.items?.length || 0)) + cursorRef.current = data.nextCursor + setDone(!data.nextCursor) + } catch (e) { + console.error('alerts reload error', e) + setDone(true) + } finally { + inFlightRef.current = false + setLoading(false) + } + }, [onCount]) + useRefreshOnVisible(reload) + useEffect(() => { + if (done || items.length === 0) return + const el = sentinelRef.current + if (!el) return + const io = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting) fetchPage() + }, { rootMargin: '200px' }) + io.observe(el) + return () => io.disconnect() + }, [items.length, done, fetchPage]) + + if (items.length === 0 && !loading) { + return ( +
+ +
+ ) + } + + return ( +
+
+ {items.map((a) => ( + +
+ {a.indicatorName} + {a.period} +
+
+ {a.sourceA} = {a.valueA} vs {a.sourceB} = {a.valueB} + 差异 {(a.diffRatio * 100).toFixed(1)}% +
+
{fmtDateTime(a.createdAt)}
+
+ ))} +
+ {loading ? : (done ? '— 已加载全部 —' : '')} +
+
+
+ ) +} diff --git a/src/pages/macro/NewsView.jsx b/src/pages/macro/NewsView.jsx new file mode 100644 index 0000000..e883f72 --- /dev/null +++ b/src/pages/macro/NewsView.jsx @@ -0,0 +1,99 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { Card, DotLoading, Empty } from 'antd-mobile' +import { api } from '../../api/client.js' +import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' +import { fmtDate } from '../../utils/format.js' + +// 新闻 (V0.2 从「首页」并入「宏观」Tab) +// - 数据: mx_finance_search_news 每日 3 点拉取国内外经济政治新闻, 按时间倒序 +// - 无限滚动: 触底自动加载下一页 +// - 详情: /macro/news/:id (挂在宏观 Tab 下, 保持底部 Tab 高亮) +export default function NewsView() { + const [items, setItems] = useState([]) + const [loading, setLoading] = useState(false) + const [done, setDone] = useState(false) + const cursorRef = useRef(null) + const sentinelRef = useRef(null) + const inFlightRef = useRef(false) + const navigate = useNavigate() + + const fetchPage = useCallback(async () => { + if (inFlightRef.current || done) return + inFlightRef.current = true + setLoading(true) + try { + const url = '/news?limit=10' + (cursorRef.current ? '&cursor=' + encodeURIComponent(cursorRef.current) : '') + const data = await api(url) + setItems((prev) => prev.concat(data.items || [])) + cursorRef.current = data.nextCursor + if (!data.nextCursor) setDone(true) + } catch (e) { + console.error('news load error', e) + setDone(true) + } finally { + inFlightRef.current = false + setLoading(false) + } + }, [done]) + + useEffect(() => { fetchPage() }, []) + // 后台回前台: 重置游标重拉第一页, 新数据到了再更新视图 + const reload = useCallback(async () => { + if (inFlightRef.current) return + inFlightRef.current = true + setLoading(true) + try { + const data = await api('/news?limit=10') + setItems(data.items || []) + cursorRef.current = data.nextCursor + setDone(!data.nextCursor) + } catch (e) { + console.error('news reload error', e) + setDone(true) + } finally { + inFlightRef.current = false + setLoading(false) + } + }, []) + useRefreshOnVisible(reload) + useEffect(() => { + if (done || items.length === 0) return + const el = sentinelRef.current + if (!el) return + const io = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting) fetchPage() + }, { rootMargin: '200px' }) + io.observe(el) + return () => io.disconnect() + }, [items.length, done, fetchPage]) + + if (items.length === 0 && !loading) { + return ( +
+ +
+ ) + } + + return ( +
+
+ {items.map((n) => ( + navigate('/macro/news/' + n.id)}> +
{n.title}
+ {n.summary &&
{n.summary}
} +
+ {n.source || '未知来源'} + {fmtDate(n.publishedAt)} + #{n.impactRank} +
+
+ ))} +
+ {loading ? : (done ? '— 已加载全部 —' : '')} +
+
+
+ ) +} diff --git a/src/styles/globals.css b/src/styles/globals.css index a778ea9..2478a80 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -67,12 +67,31 @@ input, textarea, select { font: inherit; } to { opacity: 1; transform: translateY(0); } } -/* 带有 antd-mobile NavBar 的页面 (Home/Monitor/Macro/Article) */ +/* 带有页面头部的页面 (Checkin/Monitor/Macro) */ /* height:100% 让页面占满 app-main 高度, 内部 tab-content 独立滚动, - 头部 NavBar 与分类 tab 固定在顶部不上滚 */ + 头部与分类 tab 固定在顶部不上滚 */ .page-with-navbar { display: flex; flex-direction: column; height: 100%; min-height: 0; } .page-with-navbar .adm-error-block { flex: 1; } +/* 统一主 Tab 头部 (打卡 / 宏观 / 监控): 原 NavBar 45px → 23px, 标题主色小字 + 注意: 「我」页不使用此头部, 见 MePage (顶部直接是头像区) */ +.page-header { + height: 23px; + flex: 0 0 23px; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-bg); + overflow: hidden; +} +.page-header-title { + font-size: 12px; + line-height: 1; + font-weight: 600; + letter-spacing: 1px; + color: var(--color-primary); +} + /* 底部 antd-mobile TabBar 定位 */ .app-tabbar.adm-tab-bar { position: fixed; @@ -92,11 +111,53 @@ input, textarea, select { font: inherit; } /* 宏观页 SwipeTabs */ .swipe-tabs-wrap { + position: relative; display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; } +/* tab 条右侧溢出提示: 渐变遮罩 + 箭头, 表示右边还有未展示的 tab + 遮罩宽度留足, 让被截断的 tab 文字淡出而不是被箭头压住 */ +.swipe-tabs-wrap .adm-tabs-header { + position: relative; + background: var(--color-card); +} +.swipe-tabs-wrap .adm-tabs-tab-wrapper { padding: 0 10px; } +.tab-more-hint { + position: absolute; + top: 0; + right: 0; + height: 45px; + width: 58px; + z-index: 3; + display: flex; + align-items: center; + justify-content: flex-end; + padding-right: 6px; + cursor: pointer; + background: linear-gradient(to right, rgba(255,255,255,0) 0%, #fff 62%, #fff 100%); +} +.tab-more-arrow { + display: inline-flex; + align-items: center; + justify-content: center; + width: 19px; + height: 19px; + border-radius: 50%; + background: #fff; + border: 1px solid #e3e6ea; + color: var(--color-primary); + font-size: 14px; + line-height: 1; + font-weight: 700; + box-shadow: 0 1px 4px rgba(0,0,0,.14); + animation: tab-more-nudge 1.5s ease-in-out infinite; +} +@keyframes tab-more-nudge { + 0%, 100% { transform: translateX(0); } + 50% { transform: translateX(3px); } +} .tab-content { flex: 1 1 auto; min-height: 0; @@ -200,8 +261,8 @@ input, textarea, select { font: inherit; } .login-error { background: #fef2f2; color: var(--color-danger); padding: 10px; border-radius: 6px; font-size: 13px; margin-bottom: 12px; } /* ================== 我的页 ================== */ +/* 「我」页不设顶部头部条: 头像区直接贴顶 (2026-10-23 界面调整) */ .me-page { display: flex; flex-direction: column; min-height: 100%; background: var(--color-bg); } -.me-navbar.adm-nav-bar { background: #fff; border-bottom: 1px solid var(--color-border); } .me-profile { background: linear-gradient(135deg, #1677ff, #4096ff); color: #fff; padding: 24px 16px 28px; display: flex; flex-direction: column; align-items: center; gap: 6px; } .me-avatar { background: rgba(255,255,255,.2); color: #fff; font-size: 28px; } .me-name { font-size: 18px; font-weight: 600; } @@ -210,12 +271,9 @@ input, textarea, select { font: inherit; } .me-list .adm-list-item { padding-left: 16px; padding-right: 16px; } .me-logout { color: var(--color-danger); } .me-version { text-align: center; font-size: 12px; color: var(--color-muted); padding: 18px 0 8px; } -/* 首页模块: 报警 + 新闻 */ -.home-content { padding: 0 0 16px; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } -.home-content .adm-tabs-content { --content-padding: 0; } -.home-content .tab-label { display: inline-flex; align-items: center; gap: 4px; } -.home-section { padding: 0 12px 16px; } -.section-title { font-size: 16px; font-weight: 700; padding: 14px 4px 8px; color: var(--color-text); } +/* 资讯模块: 报警 + 新闻 (V0.2 起从「首页」并入「宏观」Tab) */ +.tab-label { display: inline-flex; align-items: center; gap: 4px; } +.home-section { padding: 12px 12px 16px; } .section-list { display: flex; flex-direction: column; gap: 8px; } .alert-card.adm-card { border-radius: 10px; margin: 0; } .alert-card .adm-card-body { padding: 10px 12px; } @@ -500,3 +558,236 @@ input, textarea, select { font: inherit; } color: #fff; } .oil-day.future .oil-day-status { color: rgba(255,255,255,.85); } + +/* ================== 打卡 / 日历 / 清单 (V0.2) ================== */ +.checkin-content { padding: 0 0 16px; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } +.checkin-content .adm-tabs-content { --content-padding: 0; } + +/* ----- 环形进度 ----- */ +.ring-wrap { position: relative; display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; } +.ring-wrap.clickable { cursor: pointer; } +.ring-wrap.disabled { opacity: .5; } +.ring-svg { display: block; } +.ring-bar { transition: stroke-dashoffset 0.45s cubic-bezier(0.4, 0, 0.2, 1); } +.ring-center { + position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; + font-size: 13px; font-weight: 600; color: var(--color-text); font-variant-numeric: tabular-nums; +} +.ring-num em { font-style: normal; font-size: 10px; color: var(--color-muted); font-weight: 400; } +.ring-check { animation: ring-check-in 0.32s ease; } +@keyframes ring-check-in { from { transform: scale(0.4); opacity: 0; } to { transform: scale(1); opacity: 1; } } +.ring-over { + position: absolute; right: -2px; bottom: -2px; font-size: 10px; font-weight: 600; + color: #fff; background: #ff8f1f; border-radius: 8px; padding: 0 4px; line-height: 14px; +} + +/* ----- 完成特效 ----- */ +.celebrate { position: absolute; inset: 0; pointer-events: none; z-index: 6; } +.celebrate i { + position: absolute; left: 50%; top: 50%; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; + border-radius: 50%; animation: burst-fly 0.7s ease-out forwards; +} +@keyframes burst-fly { + 0% { transform: rotate(var(--a)) translateX(0) scale(1); opacity: 1; } + 70% { opacity: 1; } + 100% { transform: rotate(var(--a)) translateX(var(--d)) scale(0.3); opacity: 0; } +} +.celebrate-ring { + position: absolute; left: 50%; top: 50%; width: 64px; height: 64px; margin: -32px 0 0 -32px; + border-radius: 50%; border: 2px solid var(--color-primary); animation: ring-pulse 0.7s ease-out forwards; +} +@keyframes ring-pulse { + 0% { transform: scale(0.3); opacity: 0.9; } + 100% { transform: scale(1.7); opacity: 0; } +} + +/* ----- 打卡视图 ----- */ +.habit-view { padding: 0 12px 20px; } +.habit-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 8px; padding: 10px 2px; } +.habit-date { font-size: 18px; font-weight: 700; color: var(--color-text); } +.habit-summary { font-size: 12px; color: var(--color-muted); margin-top: 2px; } +.habit-summary strong { color: var(--color-primary); font-size: 14px; } +.habit-badge { margin-left: 6px; font-size: 11px; color: #ad6800; background: #fff7e6; border-radius: 4px; padding: 1px 5px; } +.habit-head-right { display: flex; gap: 6px; flex-shrink: 0; } +.checkin-empty { padding: 48px 0; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 12px; } + +.habit-list { display: flex; flex-direction: column; gap: 10px; } +.habit-card { + position: relative; background: #fff; border-radius: 10px; padding: 12px; + display: grid; grid-template-columns: auto 1fr auto; grid-template-areas: "ring main actions" "foot foot foot"; + gap: 8px 12px; box-shadow: 0 1px 2px rgba(0,0,0,.04); + transition: background 0.2s ease; +} +.habit-card:active { background: #fafafa; } +.habit-card.done { background: #f7fbf8; } +.habit-card .ring-wrap { grid-area: ring; } +.habit-main { grid-area: main; min-width: 0; } +.habit-actions { grid-area: actions; display: flex; align-items: center; } +.habit-foot { + grid-area: foot; display: flex; gap: 14px; padding-top: 8px; border-top: 1px dashed var(--color-border); +} +.habit-name { font-size: 15px; font-weight: 600; color: var(--color-text); word-break: break-all; } +.habit-meta { font-size: 12px; color: var(--color-muted); margin-top: 3px; } +.habit-streak { margin-left: 8px; color: #ff8f1f; background: rgba(255,143,31,.12); border-radius: 4px; padding: 1px 6px; } +.habit-note { font-size: 11px; color: var(--color-muted); margin-top: 3px; } +.habit-range { font-size: 11px; color: var(--color-muted); margin-top: 3px; font-variant-numeric: tabular-nums; } +.habit-over { margin-left: 8px; color: #ff8f1f; } +.habit-link { font-size: 12px; color: var(--color-primary); cursor: pointer; } +.habit-link.danger { color: var(--color-danger); } + +/* ----- 日历视图 ----- */ +.calendar-view { padding: 0 12px 20px; } +.cal-month-bar { display: flex; align-items: center; gap: 10px; padding: 8px 2px; } +.cal-month { font-size: 16px; font-weight: 700; color: var(--color-text); } +.cal-arrow { font-size: 20px; color: var(--color-muted); cursor: pointer; padding: 0 4px; line-height: 1; } +.cal-today-btn { margin-left: auto; font-size: 12px; color: var(--color-primary); cursor: pointer; } +.cal-quick { padding: 2px 0 10px; } +.cal-grid { background: #fff; border-radius: 10px; padding: 8px 6px 10px; box-shadow: 0 1px 2px rgba(0,0,0,.04); } +.cal-weekday, .cal-days { display: grid; grid-template-columns: repeat(7, 1fr); } +.cal-weekday span { text-align: center; font-size: 11px; color: var(--color-muted); padding: 4px 0; } +.cal-day { + position: relative; height: 38px; display: flex; flex-direction: column; align-items: center; + justify-content: center; border-radius: 8px; cursor: pointer; +} +.cal-day-num { font-size: 13px; color: var(--color-text); font-variant-numeric: tabular-nums; } +.cal-day.out .cal-day-num { color: #c8c8c8; } +.cal-day.future .cal-day-num { color: #b0b0b0; } +.cal-day.today .cal-day-num { color: var(--color-primary); font-weight: 700; } +.cal-day.selected { background: var(--color-primary); } +.cal-day.selected .cal-day-num { color: #fff; font-weight: 600; } +.cal-dot { width: 5px; height: 5px; border-radius: 50%; margin-top: 3px; } +.cal-dot.partial { background: #ffc53d; } +.cal-dot.full { background: #00b578; } +.cal-dot.over { background: #a855f7; } +.cal-day.selected .cal-dot { background: #fff; } +.cal-rate { font-size: 12px; color: var(--color-muted); padding: 8px 2px; } +.cal-rate strong { color: var(--color-primary); } +.cal-detail { margin-top: 6px; } +.cal-detail-title { font-size: 14px; font-weight: 600; color: var(--color-text); padding: 6px 2px 8px; } +.cal-item { + position: relative; display: flex; align-items: center; gap: 8px; background: #fff; + border-radius: 8px; padding: 10px 12px; margin-bottom: 8px; box-shadow: 0 1px 2px rgba(0,0,0,.04); +} +.cal-item.done { background: #f7fbf8; } +.cal-item-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } +.cal-item-name { font-size: 14px; font-weight: 600; color: var(--color-text); } +.cal-item-meta { font-size: 11px; color: var(--color-muted); flex: 1; text-align: right; font-variant-numeric: tabular-nums; } +.cal-item-right { flex-shrink: 0; } +.cal-item-plan { font-size: 12px; color: var(--color-muted); } + +/* ----- 清单视图 ----- */ +.list-view { padding: 0 12px 20px; } +.list-view-head { display: flex; align-items: center; gap: 8px; padding: 8px 0 10px; } +.list-view-head .adm-segmented { flex: 1; } +.checklist-wrap { display: flex; flex-direction: column; gap: 10px; } +.checklist-card { + position: relative; background: #fff; border-radius: 10px; padding: 12px; + box-shadow: 0 1px 2px rgba(0,0,0,.04); +} +.checklist-card.done { background: #f7fbf8; } +.checklist-head { cursor: pointer; } +.checklist-title-row { display: flex; align-items: center; gap: 8px; } +.checklist-title { font-size: 15px; font-weight: 600; color: var(--color-text); } +.checklist-scene { font-size: 11px; color: var(--color-primary); background: #f0f5ff; border-radius: 4px; padding: 1px 6px; } +.checklist-progress-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; } +.checklist-bar { flex: 1; height: 6px; border-radius: 3px; background: #f0f0f0; overflow: hidden; } +.checklist-bar-fill { height: 100%; border-radius: 3px; background: #00b578; transition: width 0.4s ease; } +.checklist-count { font-size: 12px; color: var(--color-muted); font-variant-numeric: tabular-nums; } +.checklist-arrow { color: var(--color-muted); transition: transform 0.2s ease; } +.checklist-arrow.open { transform: rotate(90deg); } +.checklist-body { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--color-border); } +.checklist-item { display: flex; align-items: center; gap: 8px; padding: 7px 0; } +.check-box { + width: 20px; height: 20px; border-radius: 50%; border: 1.5px solid #d0d0d0; flex-shrink: 0; + display: inline-flex; align-items: center; justify-content: center; cursor: pointer; + transition: background 0.2s ease, border-color 0.2s ease; +} +.check-box svg { opacity: 0; transition: opacity 0.15s ease; } +.checklist-item.done .check-box { background: #00b578; border-color: #00b578; } +.checklist-item.done .check-box svg { opacity: 1; } +.checklist-item-title { flex: 1; font-size: 14px; color: var(--color-text); cursor: pointer; } +.checklist-item-title em { display: block; font-style: normal; font-size: 11px; color: var(--color-muted); margin-top: 2px; } +.checklist-item.done .checklist-item-title { color: #a0a0a0; text-decoration: line-through; } +.checklist-item-del { color: #ccc; font-size: 18px; line-height: 1; padding: 0 4px; cursor: pointer; } +.checklist-add { display: flex; align-items: center; gap: 8px; margin-top: 8px; } +.checklist-add .adm-input { flex: 1; border: 1px solid var(--color-border); border-radius: 6px; padding: 6px 8px; } +.checklist-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; } + +/* ----- 模板分类 (清单的第三个分段: 进行中 / 已归档 / 模板) ----- */ +.template-wrap { display: flex; flex-direction: column; gap: 10px; } +.template-card { + background: #fff; border-radius: 10px; padding: 12px; + box-shadow: 0 1px 2px rgba(0,0,0,.04); +} +.template-head { display: flex; align-items: center; gap: 8px; } +.template-name { font-size: 15px; font-weight: 600; color: var(--color-text); word-break: break-all; } +.template-badge { + font-size: 10px; color: var(--color-muted); background: #f2f3f5; + border-radius: 4px; padding: 1px 5px; flex-shrink: 0; +} +.template-count { margin-left: auto; font-size: 11px; color: var(--color-muted); flex-shrink: 0; } +.template-items { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } +.template-item-chip { + font-size: 11px; color: var(--color-muted); background: #f7f8fa; + border-radius: 4px; padding: 2px 7px; max-width: 100%; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.template-item-chip.more { color: var(--color-primary); background: #f0f5ff; } +.template-actions { + display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; + padding-top: 8px; border-top: 1px dashed var(--color-border); +} + +/* 新建清单: 模板行 (点击后从右侧滑出选择面板) */ +.template-pick-row { + display: flex; align-items: center; gap: 8px; cursor: pointer; + border: 1px solid var(--color-border); border-radius: 8px; padding: 8px 10px; +} +.template-pick-row:active { background: #fafafa; } +.template-pick-name { flex: 1; font-size: 14px; color: var(--color-text); } +.template-pick-name.placeholder { color: #b8bcc4; } +.template-pick-meta { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--color-muted); } +.template-pick-arrow { font-size: 16px; line-height: 1; color: #c8c8c8; } + +/* 模板选择面板 (右侧滑出) */ +.tpl-picker { display: flex; flex-direction: column; height: 100%; padding: 14px 0 0; } +.tpl-picker-head { display: flex; align-items: center; padding: 0 16px 6px; } +.tpl-picker-title { font-size: 16px; font-weight: 700; color: var(--color-text); } +.tpl-picker-close { margin-left: auto; font-size: 22px; line-height: 1; color: #c8c8c8; cursor: pointer; padding: 0 4px; } +.tpl-picker-tip { font-size: 11px; color: var(--color-muted); padding: 0 16px 8px; } +.tpl-picker-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 10px 16px; } +.tpl-picker-item { + display: flex; align-items: baseline; gap: 8px; padding: 11px 10px; + border-radius: 8px; cursor: pointer; +} +.tpl-picker-item:active { background: #f5f6f8; } +.tpl-picker-item.active { background: #f0f5ff; } +.tpl-picker-name { + flex: 1; font-size: 14px; font-weight: 600; color: var(--color-text); + display: inline-flex; align-items: center; gap: 6px; word-break: break-all; +} +.tpl-picker-item.active .tpl-picker-name { color: var(--color-primary); } +.tpl-picker-sub { font-size: 11px; color: var(--color-muted); flex-shrink: 0; } +.tpl-picker-empty { padding: 40px 0; text-align: center; font-size: 13px; color: var(--color-muted); } + +/* 模板编辑弹层 */ +.tpl-edit-items { display: flex; flex-direction: column; gap: 6px; } +.tpl-edit-item { display: flex; align-items: center; gap: 8px; } +.tpl-edit-index { width: 18px; text-align: right; font-size: 11px; color: var(--color-muted); flex-shrink: 0; } +.tpl-edit-item .adm-input { + flex: 1; border: 1px solid var(--color-border); border-radius: 6px; padding: 6px 8px; +} + +/* ----- 表单弹窗 (打卡项 / 清单) ----- */ +.form-popup { padding: 16px 16px 8px; } +.form-title { font-size: 16px; font-weight: 700; color: var(--color-text); margin-bottom: 12px; } +.form-field { margin-bottom: 14px; } +.form-field > label { display: block; font-size: 12px; color: var(--color-muted); margin-bottom: 6px; } +.form-field .adm-input { border: 1px solid var(--color-border); border-radius: 8px; padding: 8px 10px; } +.form-actions { margin-top: 18px; } +.color-row { display: flex; gap: 10px; flex-wrap: wrap; } +.color-dot { + width: 26px; height: 26px; border-radius: 50%; cursor: pointer; display: inline-block; + border: 2px solid transparent; transition: transform 0.15s ease; +} +.color-dot.active { border-color: rgba(0,0,0,.25); transform: scale(1.12); } diff --git a/src/utils/format.js b/src/utils/format.js new file mode 100644 index 0000000..9f92724 --- /dev/null +++ b/src/utils/format.js @@ -0,0 +1,19 @@ +// 通用格式化工具 +export function fmtDate(s) { + if (!s) return '' + const d = new Date(s) + if (isNaN(d.getTime())) return s + const pad = (n) => String(n).padStart(2, '0') + return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) +} + +export function fmtDateTime(s) { + if (!s) return '' + const d = new Date(s) + if (isNaN(d.getTime())) return s + const pad = (n) => String(n).padStart(2, '0') + return ( + d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ) +} diff --git a/src/utils/version.js b/src/utils/version.js new file mode 100644 index 0000000..adf69fc --- /dev/null +++ b/src/utils/version.js @@ -0,0 +1,27 @@ +// 版本号比较 (纯函数, 无浏览器依赖, 方便单测) +// +// 口径: 把版本号按"非数字"切成数字字段, 逐段比较数值大小, 缺位补 0 +// "0.2.0-ota" → [0, 2, 0] +// "v1.10.3" → [1, 10, 3] +// "0.2" 与 "0.2.0" 视为相等 + +export function parseVersionFields(v) { + return String(v || '') + .trim() + .split(/[^0-9]+/) + .filter((s) => s !== '') + .map((s) => Number(s)) +} + +// a > b 返回 1, a < b 返回 -1, 相等返回 0 +export function compareVersion(a, b) { + const fa = parseVersionFields(a) + const fb = parseVersionFields(b) + const n = Math.max(fa.length, fb.length) + for (let i = 0; i < n; i++) { + const x = fa[i] || 0 + const y = fb[i] || 0 + if (x !== y) return x > y ? 1 : -1 + } + return 0 +}