This commit is contained in:
parent
a4a99789a9
commit
650f381148
29 changed files with 599 additions and 349 deletions
42
bun.lock
42
bun.lock
|
|
@ -6,6 +6,8 @@
|
|||
"dependencies": {
|
||||
"@effect/platform": "latest",
|
||||
"@effect/sql-drizzle": "latest",
|
||||
"@thi.ng/pixel": "latest",
|
||||
"@thi.ng/pixel-dither": "latest",
|
||||
"a11y-dialog": "latest",
|
||||
"drizzle-orm": "latest",
|
||||
"effect": "latest",
|
||||
|
|
@ -207,13 +209,13 @@
|
|||
|
||||
"@dual-bundle/import-meta-resolve": ["@dual-bundle/import-meta-resolve@4.1.0", "", {}, "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg=="],
|
||||
|
||||
"@effect/experimental": ["@effect/experimental@0.41.2", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.2", "@effect/platform-node": "^0.73.2", "effect": "^3.13.2", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-8FQmxE7SoZOTOqm6QB9CcdNdTN1dHqaSRlz6pijzvxsq29mWDrHqHfzixessDYVt2QTBshrel+y55Od7pndUCQ=="],
|
||||
"@effect/experimental": ["@effect/experimental@0.41.4", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.4", "@effect/platform-node": "^0.73.4", "effect": "^3.13.4", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-celrEhl/K2Eis906d3/oOv/3/w+M1jcONvrChG5qQgWYo6CJjSJm4xXopcTbiBhxAhvkd06zABkB3tr/lptUCA=="],
|
||||
|
||||
"@effect/platform": ["@effect/platform@0.77.2", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.2" } }, "sha512-stvroKHJVfjd3XhZJEPUAOgzqu9DH1vnGHIAjfs2ma6Z4qcjVpFXrxa0ZYmwRaWVIFsiADMenkN0I7XrRdAgLw=="],
|
||||
"@effect/platform": ["@effect/platform@0.77.4", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.4" } }, "sha512-3jBDn4bAWqRSKARmoMY2NmMxIMM5Py5BqJdUuYJqPyI6jbz7qkOMwgeaF62rWqiqhQLHLTgU4QL/cPwyZl7mVg=="],
|
||||
|
||||
"@effect/sql": ["@effect/sql@0.30.2", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.2", "@effect/platform": "^0.77.2", "effect": "^3.13.2" } }, "sha512-3T8dteCcXKj0jinj9OBfjUnNfqxf8S4lZ74shpRukWjmJUcnGatiy/YW1nV4lewSc0yuKLuly36A93J65AxtuQ=="],
|
||||
"@effect/sql": ["@effect/sql@0.30.4", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.4", "@effect/platform": "^0.77.4", "effect": "^3.13.4" } }, "sha512-hyGOZsNRlw09yqBcXCTr1o2+vVIVxEnBMNZzi0ZX5JbS8JBT05O7xAu3OYEzSU6bC5i+uVpJJUgdx2zDlOLkeA=="],
|
||||
|
||||
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.29.2", "", { "peerDependencies": { "@effect/sql": "^0.30.2", "drizzle-orm": "^0.31", "effect": "^3.13.2" } }, "sha512-dYxDL3954I8o+yaSzSvsmmZYlpyZgmWoy9PC8y5BpHFx/+TIEPdw5ZlSAwjDafPH3GTWo2EsSQWzVTDFC+Un2A=="],
|
||||
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.29.4", "", { "peerDependencies": { "@effect/sql": "^0.30.4", "drizzle-orm": "^0.31", "effect": "^3.13.4" } }, "sha512-Hz8RypUu5g/V3DVmD0WLK+Nqy9H/V/GA2ew41sSZhJ0fTmxaCoQ08it23qypeNN66CCJNF5uE9OWnHl10cfKGA=="],
|
||||
|
||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||
|
||||
|
|
@ -365,6 +367,22 @@
|
|||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||
|
||||
"@thi.ng/api": ["@thi.ng/api@8.11.21", "", {}, "sha512-J6BUdUtFtwZirL3M9tkCiqBXj228z7zkxWOaDWTymwBeqY9s02vJP3mQV8l5p+YPDIRmYx/q7XVuLW1UTJRN/A=="],
|
||||
|
||||
"@thi.ng/canvas": ["@thi.ng/canvas@1.0.8", "", {}, "sha512-r4bRWAsiaaNx+ihTtQDi1RQFMfOLwGgoSbZhdxLzEo6t1Ga5a/cqv/WKqUWLjD1LHjxhtflS8uPcihD2ETyAOg=="],
|
||||
|
||||
"@thi.ng/checks": ["@thi.ng/checks@3.7.1", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-RfVBQgJN0kr00SKptAAzdDOaaRtWlctqegYogynTLkUhf8Ck516Efk/macgWTzSLEPgcumN7E9F1F3lgfRCWew=="],
|
||||
|
||||
"@thi.ng/errors": ["@thi.ng/errors@2.5.27", "", {}, "sha512-t1sgGuZqHv81lzNPSRySGHnDBvtt6h3MIMn3LdZnqMR0swwOIApw6YlheMYB63u94NdtuneQYcjdvbhOsHbwPQ=="],
|
||||
|
||||
"@thi.ng/math": ["@thi.ng/math@5.11.21", "", { "dependencies": { "@thi.ng/api": "^8.11.21" } }, "sha512-JLjHdQbCpIP6F76Vq8yBtRtDOaUJ5HKsyEhU++8bjPX7VeDf8um4Ba/PUQZS+SpX0ghTZrqirqoI7H6wWz/TaQ=="],
|
||||
|
||||
"@thi.ng/pixel": ["@thi.ng/pixel@7.3.19", "", { "dependencies": { "@thi.ng/api": "^8.11.21", "@thi.ng/canvas": "^1.0.8", "@thi.ng/checks": "^3.7.1", "@thi.ng/errors": "^2.5.27", "@thi.ng/math": "^5.11.21", "@thi.ng/porter-duff": "^2.1.99" } }, "sha512-k1oUFPktxOcJEh0RuzgwoZGBkNqB1zMNvk2QILph54Cj1JJH3CcAuqJTu+N6QkXoxsgoM4DtMiDcmIdyrRokJQ=="],
|
||||
|
||||
"@thi.ng/pixel-dither": ["@thi.ng/pixel-dither@1.1.159", "", { "dependencies": { "@thi.ng/checks": "^3.7.1", "@thi.ng/math": "^5.11.21", "@thi.ng/pixel": "^7.3.19" } }, "sha512-1tQfZUPgTEe/sn0URTHbkDEtWpJyqC4WqR9BMdOcE+g+pHhp1AjGB+eru1Mhx0WKJ/jcYl669U73Qooh8L2V6A=="],
|
||||
|
||||
"@thi.ng/porter-duff": ["@thi.ng/porter-duff@2.1.99", "", { "dependencies": { "@thi.ng/api": "^8.11.21" } }, "sha512-HC4rqfHGfAMijUoNlZslRZypb7MJ8BO6XpRF2Ol/O6JqHzhHtWlwbB/WGfoSiAUfdEHprSZ3KsPCDEl7MnRdpg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
|
@ -483,7 +501,7 @@
|
|||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001700", "", {}, "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ=="],
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="],
|
||||
|
||||
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
|
||||
|
|
@ -561,9 +579,9 @@
|
|||
|
||||
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
|
||||
|
||||
"effect": ["effect@3.13.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-/w+CPqHDJ33Wq7xC4YKAchrEEPtjvxh563xH9kDTZp99seNYBoBs87vl8DJwartEjj+KLQLP8PzoDne+XmGT2A=="],
|
||||
"effect": ["effect@3.13.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-XZgCImyBpONuKdsBRk37JfV7242yxTu8r/TcL5ZELIyqRbMYa+Prr86cz1INxXi7iFXfU0havZkCJGyYp1BsiA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.104", "", {}, "sha512-Us9M2L4cO/zMBqVkJtnj353nQhMju9slHm62NprKTmdF3HH8wYOtNvDFq/JB2+ZRoGLzdvYDiATlMHs98XBM1g=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.109", "", {}, "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
|
|
@ -623,7 +641,7 @@
|
|||
|
||||
"fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", {}, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="],
|
||||
|
||||
"fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
|
||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
|
||||
|
||||
|
|
@ -885,7 +903,7 @@
|
|||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="],
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
||||
|
||||
|
|
@ -927,7 +945,7 @@
|
|||
|
||||
"strip-json-comments": ["strip-json-comments@5.0.1", "", {}, "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw=="],
|
||||
|
||||
"stylelint": ["stylelint@16.14.1", "", { "dependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/media-query-list-parser": "^4.0.2", "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", "css-tree": "^3.1.0", "debug": "^4.3.7", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^10.0.5", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.1", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "supports-hyperlinks": "^3.1.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^5.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw=="],
|
||||
"stylelint": ["stylelint@16.15.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/media-query-list-parser": "^4.0.2", "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", "css-tree": "^3.1.0", "debug": "^4.3.7", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^10.0.6", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.3", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "supports-hyperlinks": "^3.2.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^5.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-OK6Rs7EPdcdmjqiDycadZY4fw3f5/TC1X6/tGjnF3OosbwCeNs7nG+79MCAtjEg7ckwqTJTsku08e0Rmaz5nUw=="],
|
||||
|
||||
"stylelint-config-clean-order": ["stylelint-config-clean-order@7.0.0", "", { "dependencies": { "stylelint-order": "^6.0.4" }, "peerDependencies": { "stylelint": ">=14" } }, "sha512-R28w1xNliIbem3o+VIrNjAU8cMgxrGlDoXVqWW7lJ1OvSDsmNGj5aKSW6Xm7i5PK4E99T3Hs19BJFni5IbE56g=="],
|
||||
|
||||
|
|
@ -969,13 +987,13 @@
|
|||
|
||||
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
|
||||
|
||||
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
||||
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.25.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.25.0", "@typescript-eslint/parser": "8.25.0", "@typescript-eslint/utils": "8.25.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-TxRdQQLH4g7JkoFlYG3caW5v1S6kEkz8rqt80iQJZUYPq1zD1Ra7HfQBJJ88ABRaMvHAXnwRvRB4V+6sQ9xN5Q=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="],
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@
|
|||
},
|
||||
"newLineKind": "lf",
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.93.4.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.4.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.4.wasm",
|
||||
"https://plugins.dprint.dev/typescript-0.94.0.wasm",
|
||||
"https://plugins.dprint.dev/json-0.20.0.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.18.0.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.7.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/malva-v0.11.1.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.18.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.19.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm",
|
||||
"https://plugins.dprint.dev/exec-0.5.1.json@492414e39dea4dccc07b4af796d2f4efdb89e84bae2bd4e1e924c0cc050855bf"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
import { defineConfigWithVueTs, vueTsConfigs } from "@vue/eslint-config-typescript";
|
||||
import perfectionist from "eslint-plugin-perfectionist";
|
||||
import vue from "eslint-plugin-vue";
|
||||
import globals from "globals";
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
files: ["**/*.{js,mjs,ts,mts,vue}"],
|
||||
languageOptions: { ecmaVersion: "latest", globals: { ...globals.browser, ...globals.es2025 } },
|
||||
name: "app/files-to-lint",
|
||||
},
|
||||
{ ignores: [".cache/", "dist/", "node_modules/"], name: "app/files-to-ignore" },
|
||||
vueTsConfigs.strictTypeChecked,
|
||||
vueTsConfigs.stylisticTypeChecked,
|
||||
vue.configs["flat/recommended"],
|
||||
{
|
||||
name: "app/no-vue-formatting",
|
||||
rules: {
|
||||
"vue/array-bracket-newline": "off",
|
||||
"vue/array-bracket-spacing": "off",
|
||||
"vue/array-element-newline": "off",
|
||||
"vue/arrow-spacing": "off",
|
||||
"vue/attributes-order": ["error", { alphabetical: true }],
|
||||
"vue/block-spacing": "off",
|
||||
"vue/block-tag-newline": "off",
|
||||
"vue/brace-style": "off",
|
||||
"vue/comma-dangle": "off",
|
||||
"vue/comma-spacing": "off",
|
||||
"vue/comma-style": "off",
|
||||
"vue/dot-location": "off",
|
||||
"vue/first-attribute-linebreak": "off",
|
||||
"vue/func-call-spacing": "off",
|
||||
"vue/html-closing-bracket-newline": "off",
|
||||
"vue/html-closing-bracket-spacing": "off",
|
||||
"vue/html-comment-content-newline": "off",
|
||||
"vue/html-comment-content-spacing": "off",
|
||||
"vue/html-comment-indent": "off",
|
||||
"vue/html-indent": "off",
|
||||
"vue/html-quotes": "off",
|
||||
"vue/html-self-closing": "off",
|
||||
"vue/key-spacing": "off",
|
||||
"vue/keyword-spacing": "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/max-len": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/multiline-ternary": "off",
|
||||
"vue/new-line-between-multi-line-property": "off",
|
||||
"vue/no-extra-parens": "off",
|
||||
"vue/no-multi-spaces": "off",
|
||||
"vue/no-spaces-around-equal-signs-in-attribute": "off",
|
||||
"vue/object-curly-newline": "off",
|
||||
"vue/object-curly-spacing": "off",
|
||||
"vue/object-property-newline": "off",
|
||||
"vue/operator-linebreak": "off",
|
||||
"vue/padding-line-between-blocks": "off",
|
||||
"vue/padding-line-between-tags": "off",
|
||||
"vue/padding-lines-in-component-definition": "off",
|
||||
"vue/quote-props": "off",
|
||||
"vue/script-indent": "off",
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/space-in-parens": "off",
|
||||
"vue/space-infix-ops": "off",
|
||||
"vue/space-unary-ops": "off",
|
||||
"vue/template-curly-spacing": "off",
|
||||
"vue/v-for-delimiter-style": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ts/no-annoying-rules",
|
||||
rules: {
|
||||
"@typescript-eslint/no-misused-spread": "off",
|
||||
},
|
||||
},
|
||||
perfectionist.configs["recommended-natural"],
|
||||
);
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
"AUTOINCREMENT",
|
||||
"vtsls",
|
||||
"quartary",
|
||||
"fieldset"
|
||||
"fieldset",
|
||||
"tabindex"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
4
justfile
4
justfile
|
|
@ -2,8 +2,8 @@ set shell := ["fish", "-c"]
|
|||
|
||||
# Variables de chemins de configuration.
|
||||
|
||||
drizzleConfigFile := "src/db/drizzle.config.ts"
|
||||
esLintConfigFile := "cfg/eslint.config.mts"
|
||||
drizzleConfigFile := "cfg/drizzle.config.ts"
|
||||
esLintConfigFile := "eslint.config.mts"
|
||||
esLintCssConfigFile := "cfg/eslint-css.config.mts"
|
||||
knipConfigFile := "cfg/knip.config.ts"
|
||||
prettierConfigFile := "cfg/prettier.config.mjs"
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -4,11 +4,13 @@
|
|||
"type": "module",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@effect/platform": "^0.77.2",
|
||||
"@effect/sql-drizzle": "^0.29.2",
|
||||
"@effect/platform": "^0.77.4",
|
||||
"@effect/sql-drizzle": "^0.29.4",
|
||||
"@thi.ng/pixel": "^7.3.19",
|
||||
"@thi.ng/pixel-dither": "^1.1.159",
|
||||
"a11y-dialog": "^8.1.1",
|
||||
"drizzle-orm": "^0.40.0",
|
||||
"effect": "^3.13.2",
|
||||
"effect": "^3.13.4",
|
||||
"pinia": "^3.0.1",
|
||||
"sqlocal": "^0.14.0",
|
||||
"vue": "^3.5.13",
|
||||
|
|
@ -34,13 +36,13 @@
|
|||
"prettier": "^3.5.2",
|
||||
"prettier-plugin-pkg": "^0.18.1",
|
||||
"prettier-plugin-sh": "^0.15.0",
|
||||
"stylelint": "^16.14.1",
|
||||
"stylelint": "^16.15.0",
|
||||
"stylelint-config-clean-order": "^7.0.0",
|
||||
"stylelint-config-standard": "^37.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "^2.8.0",
|
||||
"stylelint-plugin-logical-css": "^1.2.1",
|
||||
"tsr": "^1.3.4",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.0",
|
||||
"vue-tsc": "^2.2.4"
|
||||
}
|
||||
|
|
|
|||
10
src/components/EntryEditDialog.vue
Normal file
10
src/components/EntryEditDialog.vue
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import type { TmdbMovieSearchResponseResult } from "@/libs/apis/tmdb/schemas";
|
||||
|
||||
defineProps<{
|
||||
entryId: number;
|
||||
tmdbSearchData: TmdbMovieSearchResponseResult;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template></template>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import Search from "@/libs/search/search.ts";
|
||||
import { Effect, pipe } from "effect";
|
||||
import { useTemplateRef } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
|
@ -21,15 +22,14 @@
|
|||
toggleDialog();
|
||||
};
|
||||
|
||||
const redirectToSearch = async (event: Event): void => {
|
||||
const redirectToSearch = async (event: Event): Promise<void> => {
|
||||
if (!form.value?.checkValidity()) return;
|
||||
event.preventDefault();
|
||||
|
||||
await pipe(
|
||||
Effect.fromNullable(form.value),
|
||||
Effect.andThen((form: HTMLFormElement) => new FormData(form)),
|
||||
Effect.andThen((formData: FormData) => new URLSearchParams(formData)),
|
||||
Effect.andThen((searchParams: URLSearchParams) => Object.fromEntries(searchParams.entries())),
|
||||
Effect.andThen((formData: FormData) => Search.formDataToRecord(formData)),
|
||||
Effect.tap(query => router.push({ path: "/search", query })),
|
||||
Effect.runPromise,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,30 +1,59 @@
|
|||
<script setup lang="ts">
|
||||
import type { TmdbMovieSearchResponse } from "@/libs/apis/tmdb/schemas";
|
||||
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
||||
import type { Values } from "@/libs/utils/types";
|
||||
|
||||
const { searchResults } = defineProps<{
|
||||
searchResults: typeof TmdbMovieSearchResponse.Type.results | undefined;
|
||||
}>();
|
||||
import { tupleByTitle } from "@/libs/apis/tmdb/orders.ts";
|
||||
import { Array as Arr, Match, pipe } from "effect";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
const SORT_ORDERS = {
|
||||
ORIGINAL: "original",
|
||||
POPULARITY: "popularity",
|
||||
RELEASE_DATE: "release_date",
|
||||
TITLE: "title",
|
||||
} as const;
|
||||
|
||||
const { searchData } = defineProps<{ searchData: Map<number, MergedTmdbLocalData> }>();
|
||||
const sortOrder = ref<Values<typeof SORT_ORDERS>>(SORT_ORDERS.TITLE);
|
||||
|
||||
const sortedData = computed(() =>
|
||||
pipe(
|
||||
Array.from(searchData.entries()),
|
||||
(result: [number, MergedTmdbLocalData][]) =>
|
||||
Match.value(sortOrder.value).pipe(
|
||||
Match.when(SORT_ORDERS.ORIGINAL, () => result),
|
||||
Match.when(SORT_ORDERS.POPULARITY, () => Arr.sort(result, tupleByTitle)),
|
||||
Match.when(SORT_ORDERS.RELEASE_DATE, () => Arr.sort(result, tupleByTitle)),
|
||||
Match.when(SORT_ORDERS.TITLE, () => Arr.sort(result, tupleByTitle)),
|
||||
Match.exhaustive,
|
||||
),
|
||||
)
|
||||
);
|
||||
console.debug(sortedData.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p v-show="!searchResults || searchResults?.length === 0">/</p>
|
||||
<table v-show="searchResults?.length">
|
||||
<p v-show="!sortedData || sortedData?.length === 0">/</p>
|
||||
<table v-show="sortedData?.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Nom</th>
|
||||
<th scope="col">Année</th>
|
||||
<th scope="col">Popularité</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="result in searchResults" :key="result.id" class="row-link"
|
||||
role="button"
|
||||
v-for="result in sortedData" :key="result[0]" class="row-link"
|
||||
:data-artwork-id="result[1].artWorkId" :data-entry-id="result[1].entryId" :data-tmdb-id="result[0]"
|
||||
tabindex="0"
|
||||
>
|
||||
<th class="name" scope="row">
|
||||
{{ result.original_title }}
|
||||
{{ result[1].original_title }}
|
||||
</th>
|
||||
<td class="release-date">{{ result.release_date }}</td>
|
||||
<td class="release-date">{{ result[1].release_date }}</td>
|
||||
<td class="popularite">{{ result[1].popularity }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -44,7 +73,7 @@
|
|||
}
|
||||
|
||||
thead {
|
||||
border-block-end: 1px solid var(--color-primary);
|
||||
border-block-end: 1px solid var(--root-text-color);
|
||||
|
||||
tr > * + * {
|
||||
padding-inline-start: var(--s-2);
|
||||
|
|
@ -71,13 +100,13 @@
|
|||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-secondary);
|
||||
background: var(--root-text-color);
|
||||
color: var(--root-background-color);
|
||||
}
|
||||
|
||||
&:active {
|
||||
outline: 1px solid var(--color-secondary);
|
||||
outline-offset: -0.1rem;
|
||||
outline: 2px solid var(--root-background-color);
|
||||
outline-offset: -0.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,15 @@
|
|||
CREATE TABLE `art_works` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`medium_type_id` integer,
|
||||
`name` text NOT NULL,
|
||||
`release_date` text(10) NOT NULL,
|
||||
FOREIGN KEY (`medium_type_id`) REFERENCES `media_types`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `diary_entries` (
|
||||
`art_work_id` integer,
|
||||
`art_work_id` integer NOT NULL,
|
||||
`date_created` text(10) NOT NULL,
|
||||
`date_modified` text(10) NOT NULL,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`state_id` integer,
|
||||
`user_id` integer,
|
||||
`state_id` integer NOT NULL,
|
||||
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
FOREIGN KEY (`state_id`) REFERENCES `diary_entries_states`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||
FOREIGN KEY (`state_id`) REFERENCES `diary_entries_states`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `diary_entries_genres` (
|
||||
`art_work_id` integer,
|
||||
`art_work_id` integer NOT NULL,
|
||||
`genre_id` integer,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
|
|
@ -32,6 +22,25 @@ CREATE TABLE `diary_entries_states` (
|
|||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `diary_entries_states_state_unique` ON `diary_entries_states` (`state`);--> statement-breakpoint
|
||||
CREATE TABLE `viewings` (
|
||||
`art_work_id` integer NOT NULL,
|
||||
`date` text(10) NOT NULL,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `art_works` (
|
||||
`cover_path` text,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`medium_type_id` integer NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`release_date` text(10) NOT NULL,
|
||||
`tmdb_id` integer NOT NULL,
|
||||
FOREIGN KEY (`medium_type_id`) REFERENCES `media_types`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `art_works_cover_path_unique` ON `art_works` (`cover_path`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `art_works_tmdb_id_unique` ON `art_works` (`tmdb_id`);--> statement-breakpoint
|
||||
CREATE TABLE `genres` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
|
|
@ -47,20 +56,4 @@ CREATE TABLE `media_types` (
|
|||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `media_types_name_unique` ON `media_types` (`name`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `media_types_slug_unique` ON `media_types` (`slug`);--> statement-breakpoint
|
||||
CREATE TABLE `users` (
|
||||
`email` text NOT NULL,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `users_name_unique` ON `users` (`name`);--> statement-breakpoint
|
||||
CREATE TABLE `viewings` (
|
||||
`art_work_id` integer,
|
||||
`date` text(10) NOT NULL,
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` integer,
|
||||
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
CREATE UNIQUE INDEX `media_types_slug_unique` ON `media_types` (`slug`);
|
||||
|
|
@ -1,45 +1,9 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "7f64c80e-6fd1-4f8c-859c-dac137206238",
|
||||
"id": "da9e1cf6-aba7-4b5a-a839-3b0fe3dce876",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"art_works": {
|
||||
"name": "art_works",
|
||||
"columns": {
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"medium_type_id": {
|
||||
"name": "medium_type_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false },
|
||||
"release_date": {
|
||||
"name": "release_date",
|
||||
"type": "text(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"art_works_medium_type_id_media_types_id_fk": {
|
||||
"name": "art_works_medium_type_id_media_types_id_fk",
|
||||
"tableFrom": "art_works",
|
||||
"tableTo": "media_types",
|
||||
"columnsFrom": ["medium_type_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"diary_entries": {
|
||||
"name": "diary_entries",
|
||||
"columns": {
|
||||
|
|
@ -47,7 +11,7 @@
|
|||
"name": "art_work_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"date_created": {
|
||||
|
|
@ -64,19 +28,18 @@
|
|||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"state_id": {
|
||||
"name": "state_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
|
|
@ -86,8 +49,12 @@
|
|||
"name": "diary_entries_art_work_id_art_works_id_fk",
|
||||
"tableFrom": "diary_entries",
|
||||
"tableTo": "art_works",
|
||||
"columnsFrom": ["art_work_id"],
|
||||
"columnsTo": ["id"],
|
||||
"columnsFrom": [
|
||||
"art_work_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -95,17 +62,12 @@
|
|||
"name": "diary_entries_state_id_diary_entries_states_id_fk",
|
||||
"tableFrom": "diary_entries",
|
||||
"tableTo": "diary_entries_states",
|
||||
"columnsFrom": ["state_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"diary_entries_user_id_users_id_fk": {
|
||||
"name": "diary_entries_user_id_users_id_fk",
|
||||
"tableFrom": "diary_entries",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": ["user_id"],
|
||||
"columnsTo": ["id"],
|
||||
"columnsFrom": [
|
||||
"state_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -121,7 +83,7 @@
|
|||
"name": "art_work_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"genre_id": {
|
||||
|
|
@ -131,7 +93,13 @@
|
|||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true }
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
|
|
@ -139,8 +107,12 @@
|
|||
"name": "diary_entries_genres_art_work_id_art_works_id_fk",
|
||||
"tableFrom": "diary_entries_genres",
|
||||
"tableTo": "art_works",
|
||||
"columnsFrom": ["art_work_id"],
|
||||
"columnsTo": ["id"],
|
||||
"columnsFrom": [
|
||||
"art_work_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -148,8 +120,12 @@
|
|||
"name": "diary_entries_genres_genre_id_genres_id_fk",
|
||||
"tableFrom": "diary_entries_genres",
|
||||
"tableTo": "genres",
|
||||
"columnsFrom": ["genre_id"],
|
||||
"columnsTo": ["id"],
|
||||
"columnsFrom": [
|
||||
"genre_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -161,13 +137,27 @@
|
|||
"diary_entries_states": {
|
||||
"name": "diary_entries_states",
|
||||
"columns": {
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"state": { "name": "state", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"state": {
|
||||
"name": "state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"diary_entries_states_state_unique": {
|
||||
"name": "diary_entries_states_state_unique",
|
||||
"columns": ["state"],
|
||||
"columns": [
|
||||
"state"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
|
|
@ -176,54 +166,6 @@
|
|||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"genres": {
|
||||
"name": "genres",
|
||||
"columns": {
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false },
|
||||
"slug": { "name": "slug", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }
|
||||
},
|
||||
"indexes": {
|
||||
"genres_name_unique": { "name": "genres_name_unique", "columns": ["name"], "isUnique": true },
|
||||
"genres_slug_unique": { "name": "genres_slug_unique", "columns": ["slug"], "isUnique": true }
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"media_types": {
|
||||
"name": "media_types",
|
||||
"columns": {
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false },
|
||||
"slug": { "name": "slug", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }
|
||||
},
|
||||
"indexes": {
|
||||
"media_types_name_unique": { "name": "media_types_name_unique", "columns": ["name"], "isUnique": true },
|
||||
"media_types_slug_unique": { "name": "media_types_slug_unique", "columns": ["slug"], "isUnique": true }
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"users": {
|
||||
"name": "users",
|
||||
"columns": {
|
||||
"email": { "name": "email", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false },
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }
|
||||
},
|
||||
"indexes": {
|
||||
"users_email_unique": { "name": "users_email_unique", "columns": ["email"], "isUnique": true },
|
||||
"users_name_unique": { "name": "users_name_unique", "columns": ["name"], "isUnique": true }
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"viewings": {
|
||||
"name": "viewings",
|
||||
"columns": {
|
||||
|
|
@ -231,17 +173,22 @@
|
|||
"name": "art_work_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"date": { "name": "date", "type": "text(10)", "primaryKey": false, "notNull": true, "autoincrement": false },
|
||||
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"date": {
|
||||
"name": "date",
|
||||
"type": "text(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
|
|
@ -250,17 +197,12 @@
|
|||
"name": "viewings_art_work_id_art_works_id_fk",
|
||||
"tableFrom": "viewings",
|
||||
"tableTo": "art_works",
|
||||
"columnsFrom": ["art_work_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"viewings_user_id_users_id_fk": {
|
||||
"name": "viewings_user_id_users_id_fk",
|
||||
"tableFrom": "viewings",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": ["user_id"],
|
||||
"columnsTo": ["id"],
|
||||
"columnsFrom": [
|
||||
"art_work_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -268,10 +210,189 @@
|
|||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"art_works": {
|
||||
"name": "art_works",
|
||||
"columns": {
|
||||
"cover_path": {
|
||||
"name": "cover_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"medium_type_id": {
|
||||
"name": "medium_type_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"release_date": {
|
||||
"name": "release_date",
|
||||
"type": "text(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tmdb_id": {
|
||||
"name": "tmdb_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"art_works_cover_path_unique": {
|
||||
"name": "art_works_cover_path_unique",
|
||||
"columns": [
|
||||
"cover_path"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"art_works_tmdb_id_unique": {
|
||||
"name": "art_works_tmdb_id_unique",
|
||||
"columns": [
|
||||
"tmdb_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"art_works_medium_type_id_media_types_id_fk": {
|
||||
"name": "art_works_medium_type_id_media_types_id_fk",
|
||||
"tableFrom": "art_works",
|
||||
"tableTo": "media_types",
|
||||
"columnsFrom": [
|
||||
"medium_type_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"genres": {
|
||||
"name": "genres",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"genres_name_unique": {
|
||||
"name": "genres_name_unique",
|
||||
"columns": [
|
||||
"name"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"genres_slug_unique": {
|
||||
"name": "genres_slug_unique",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"media_types": {
|
||||
"name": "media_types",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"media_types_name_unique": {
|
||||
"name": "media_types_name_unique",
|
||||
"columns": [
|
||||
"name"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"media_types_slug_unique": {
|
||||
"name": "media_types_slug_unique",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": { "schemas": {}, "tables": {}, "columns": {} },
|
||||
"internal": { "indexes": {} }
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [{ "idx": 0, "version": "6", "when": 1739953332871, "tag": "0000_perfect_justice", "breakpoints": true }]
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1740666437095,
|
||||
"tag": "0000_open_the_twelve",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ export const MediaTypes = table("media_types", {
|
|||
});
|
||||
|
||||
export const ArtWorks = table("art_works", {
|
||||
coverPath: t.text("cover_path").unique(),
|
||||
id: t.integer("id").primaryKey({ autoIncrement: true }),
|
||||
mediumTypeId: t.integer("medium_type_id").references((): AnySQLiteColumn => MediaTypes.id).notNull(),
|
||||
name: t.text("name").notNull(),
|
||||
releaseDate: t.text("release_date", { length: 10 }).notNull(),
|
||||
tmdbId: t.integer("tmdb_id").notNull().unique(),
|
||||
});
|
||||
|
||||
export const Genres = table("genres", {
|
||||
|
|
@ -31,17 +33,28 @@ export const Genres = table("genres", {
|
|||
// Schémas
|
||||
|
||||
export const MediaTypeSchema = Schema.Struct({
|
||||
/** L'ID numérique du type de média. */
|
||||
id: Schema.NonNegativeInt,
|
||||
/** Le nom du type de média. */
|
||||
name: Schema.NonEmptyString,
|
||||
/** L'ID alphabétique du type de média. */
|
||||
slug: Schema.Enums(MEDIA_TYPES),
|
||||
});
|
||||
export type MediaType = Schema.Schema.Type<typeof MediaTypeSchema>;
|
||||
|
||||
export const ArtWorkSchema = Schema.Struct({
|
||||
/** Le chemin de l'image de la pochette de l'oeuvre d'art. */
|
||||
coverPath: Schema.Union(Schema.String, Schema.Null),
|
||||
/** L'ID numérique de l'ouvre d'art. */
|
||||
id: Schema.NonNegativeInt,
|
||||
/** L'ID numérique du type de l'oeuvre d'art. */
|
||||
mediumTypeId: Schema.NonNegativeInt,
|
||||
/** Le nom de l'oeuvre d'art. */
|
||||
name: Schema.NonEmptyString,
|
||||
/** La date de sortie de l'oeuvre d'art au format YYYY-MM-DD. */
|
||||
releaseDate: Schema.String,
|
||||
/** L'ID numérique de l'oeuvre sur la base de données TMDB. */
|
||||
tmdbId: Schema.NonNegativeInt,
|
||||
});
|
||||
export type ArtWork = Schema.Schema.Type<typeof ArtWorkSchema>;
|
||||
|
||||
|
|
|
|||
22
src/libs/apis/tmdb/orders.ts
Normal file
22
src/libs/apis/tmdb/orders.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
||||
|
||||
import { Order } from "effect";
|
||||
|
||||
import type { TmdbMovieSearchResponseResult } from "./schemas";
|
||||
|
||||
export const byTitle = Order.mapInput(
|
||||
Order.string,
|
||||
(tmdbEntry: TmdbMovieSearchResponseResult) => tmdbEntry.original_title,
|
||||
);
|
||||
|
||||
export const byReleaseDate = Order.mapInput(
|
||||
Order.string,
|
||||
(tmdbEntry: TmdbMovieSearchResponseResult) => tmdbEntry.release_date,
|
||||
);
|
||||
|
||||
// Tuples
|
||||
|
||||
export const tupleByTitle = Order.mapInput(
|
||||
Order.string,
|
||||
(data: [number, MergedTmdbLocalData]) => data[1].title,
|
||||
);
|
||||
|
|
@ -25,8 +25,9 @@ export class TmdbMovieSearchQueryParams extends Schema.Class<TmdbMovieSearchQuer
|
|||
}) {}
|
||||
|
||||
// Réponse
|
||||
export class TmdbMovieSearchResponseResults
|
||||
extends Schema.Class<TmdbMovieSearchResponseResults>("TmdbMovieSearchResponseResults")({
|
||||
|
||||
export class TmdbMovieSearchResponseResult
|
||||
extends Schema.Class<TmdbMovieSearchResponseResult>("TmdbMovieSearchResponseResult")({
|
||||
adult: Schema.Boolean,
|
||||
backdrop_path: Schema.Union(Schema.String, Schema.Null),
|
||||
genre_ids: Schema.Array(Schema.NonNegativeInt),
|
||||
|
|
@ -46,7 +47,7 @@ export class TmdbMovieSearchResponseResults
|
|||
|
||||
export class TmdbMovieSearchResponse extends Schema.Class<TmdbMovieSearchResponse>("TmdbMovieSearchResponse")({
|
||||
page: Schema.NonNegativeInt,
|
||||
results: Schema.Array(TmdbMovieSearchResponseResults),
|
||||
results: Schema.Array(TmdbMovieSearchResponseResult),
|
||||
total_pages: Schema.NonNegativeInt,
|
||||
total_results: Schema.NonNegativeInt,
|
||||
}) {}
|
||||
|
|
|
|||
|
|
@ -6,3 +6,21 @@ export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("
|
|||
type: Schema.Enums(MEDIA_TYPES),
|
||||
year: Schema.String,
|
||||
}) {}
|
||||
|
||||
export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("MergedTmdbLocalData")({
|
||||
artWorkCoverPath: Schema.Union(Schema.String, Schema.Null),
|
||||
artWorkId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||
artWorkMediumTypeId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||
entryDateCreated: Schema.String.pipe(Schema.optional),
|
||||
entryDateModified: Schema.String.pipe(Schema.optional),
|
||||
entryId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||
entryStateId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||
genre_ids: Schema.Array(Schema.NonNegativeInt),
|
||||
original_language: Schema.String,
|
||||
original_title: Schema.String,
|
||||
overview: Schema.String,
|
||||
popularity: Schema.Number,
|
||||
release_date: Schema.String,
|
||||
title: Schema.String,
|
||||
tmdbId: Schema.NonNegativeInt,
|
||||
}) {}
|
||||
|
|
|
|||
10
src/libs/search/types.d.ts
vendored
Normal file
10
src/libs/search/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { ArtWork, DiaryEntry } from "@/db/schemas";
|
||||
|
||||
import type { TmdbMovieSearchResponseResult } from "../apis/tmdb/schemas";
|
||||
|
||||
/** Page de réponse de l'API TMDB avec les données locales correspondantes. */
|
||||
export interface TmdbDataWithLocalData {
|
||||
artWork?: ArtWork;
|
||||
entry?: DiaryEntry;
|
||||
tmdbData: TmdbMovieSearchResponseResult;
|
||||
}
|
||||
|
|
@ -13,3 +13,5 @@ export const singleResultOrFail = <A, E>(orFail: () => E) =>
|
|||
Effect.mapError(_ => orFail()),
|
||||
)
|
||||
);
|
||||
|
||||
export const getOrUndefined = <A, E, R>(a: Effect.Effect<A, E, R>) => Effect.orElseSucceed(a, () => undefined);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import type { NonEmptyArray } from "effect/Array";
|
||||
import type { Ref } from "vue";
|
||||
import type { ComputedRef } from "vue";
|
||||
|
||||
import ErrorMessage from "@/components/ErrorMessage.vue";
|
||||
import LoadingMessage from "@/components/LoadingMessage.vue";
|
||||
|
|
@ -11,47 +10,39 @@
|
|||
import {
|
||||
TmdbMovieSearchQueryParams,
|
||||
TmdbMovieSearchResponse,
|
||||
TmdbMovieSearchResponseResults,
|
||||
TmdbMovieSearchResponseResult,
|
||||
} from "@/libs/apis/tmdb/schemas.ts";
|
||||
import { SearchPageQueryParams } from "@/libs/search/schemas.ts";
|
||||
import { MergedTmdbLocalData, SearchPageQueryParams } from "@/libs/search/schemas.ts";
|
||||
import Search from "@/libs/search/search.ts";
|
||||
import { getCurrentYear } from "@/libs/utils/dates.ts";
|
||||
import { getOrUndefined } from "@/libs/utils/effects.ts";
|
||||
import { PrettyLogger } from "@/services/logger.ts";
|
||||
import { ReadApi } from "@/services/read-api.ts";
|
||||
import { RuntimeClient } from "@/services/runtime-client.ts";
|
||||
import { TmdbApi } from "@/services/tmdb-api.ts";
|
||||
import { Effect, pipe, Schema } from "effect";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { watch } from "vue";
|
||||
import { useTemplateRef } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
/*
|
||||
* Le formulaire reçoit en valeurs initiales les paramètres de l'URL.
|
||||
* Quand le formulaire est soumis, les paramètres de l'URL sont mis à jour.
|
||||
* Un effet est déclenché à chaque mise à jour des paramètres d'URL : une nouvelle recherche est déclenchée et les résultats sont mis à jour.
|
||||
*/
|
||||
import { Array as Arr, Effect, pipe, Schema } from "effect";
|
||||
import { computed, onMounted, ref, useTemplateRef, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
// États
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
/** L'année courante pour la limite supérieure du champs Année de la recherché. */
|
||||
const currentYear: number = getCurrentYear();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
/** Effet des paramètres validés de la route. */
|
||||
const routeQueryParams = computed(() => Schema.decodeUnknown(SearchPageQueryParams)(route.query));
|
||||
|
||||
/** Le formulaire de recherche. */
|
||||
const form = useTemplateRef("form");
|
||||
/** Les valeurs du formulaire de recherche. */
|
||||
const searchFormData: Ref<SearchPageQueryParams | undefined> = ref<SearchPageQueryParams>();
|
||||
|
||||
/** Le retour de la requête de recherche de films auprès de l'API TMDB. */
|
||||
const search: Ref<TmdbMovieSearchResponse | undefined> = ref<TmdbMovieSearchResponse>();
|
||||
/** Les résultats de la requête de recherche de films auprès de l'API TMDB. */
|
||||
const searchResults: ComputedRef<readonly TmdbMovieSearchResponseResults[] | undefined> = computed(() =>
|
||||
search.value?.results
|
||||
);
|
||||
|
||||
const searchData: Ref<Map<number, MergedTmdbLocalData>> = ref(new Map<number, MergedTmdbLocalData>());
|
||||
|
||||
/** État du chargement de la requête auprès de l'API TMDB. */
|
||||
const isLoading: Ref<boolean> = ref(false);
|
||||
|
|
@ -143,6 +134,51 @@
|
|||
|
||||
// Cycles
|
||||
|
||||
watch(search, async (): Promise<void> => {
|
||||
await RuntimeClient.runPromise(Effect.gen(function*() {
|
||||
searchData.value.clear();
|
||||
const results = search.value?.results ?? [];
|
||||
const readApi = yield* ReadApi;
|
||||
|
||||
void results.map((result: TmdbMovieSearchResponseResult) =>
|
||||
Effect.gen(function*() {
|
||||
const entry = yield* pipe(
|
||||
readApi.getEntryByTmdbId(result.id),
|
||||
effect => getOrUndefined(effect),
|
||||
);
|
||||
|
||||
const artWork = yield* pipe(
|
||||
readApi.getArtworkByTmdbId(result.id),
|
||||
effect => getOrUndefined(effect),
|
||||
);
|
||||
|
||||
searchData.value.set(
|
||||
result.id,
|
||||
yield* Schema.decodeUnknown(MergedTmdbLocalData)(
|
||||
{
|
||||
artWorkCoverPath: result.poster_path,
|
||||
artWorkId: artWork?.id,
|
||||
artWorkMediumTypeId: artWork?.mediumTypeId,
|
||||
entryDateCreated: entry?.dateCreated,
|
||||
entryDateModified: entry?.dateModified,
|
||||
entryId: entry?.id,
|
||||
entryStateId: entry?.stateId,
|
||||
genre_ids: result.genre_ids,
|
||||
original_language: result.original_language,
|
||||
original_title: result.original_title,
|
||||
overview: result.overview,
|
||||
popularity: result.popularity,
|
||||
release_date: result.release_date,
|
||||
title: result.title,
|
||||
tmdbId: result.id,
|
||||
} satisfies MergedTmdbLocalData,
|
||||
),
|
||||
);
|
||||
}).pipe(Effect.runPromise)
|
||||
);
|
||||
}));
|
||||
});
|
||||
|
||||
watch(route, async (): Promise<void> => {
|
||||
await updateSearchResults();
|
||||
}, { immediate: true });
|
||||
|
|
@ -215,7 +251,7 @@
|
|||
|
||||
<LoadingMessage v-if="isLoading">Récupération des résultats</LoadingMessage>
|
||||
<ErrorMessage v-if="isErrored">{{ message }}</ErrorMessage>
|
||||
<TmdbSearchResults v-else :search-results="searchResults"></TmdbSearchResults>
|
||||
<TmdbSearchResults v-else :search-data="searchData"></TmdbSearchResults>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { SQLocalDrizzle } from "sqlocal/drizzle";
|
||||
|
||||
import v0000 from "@/db/drizzle/0000_perfect_justice.sql?raw";
|
||||
import v0000 from "@/db/drizzle/0000_open_the_twelve.sql?raw";
|
||||
import { Data, Effect } from "effect";
|
||||
|
||||
import { LocalSqlite } from "./db";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DiaryEntries } from "@/db/schemas";
|
||||
import { ArtWorks, DiaryEntries } from "@/db/schemas";
|
||||
import { singleResultOrFail } from "@/libs/utils/effects";
|
||||
import { desc } from "drizzle-orm";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { Data, Effect } from "effect";
|
||||
|
||||
import { LocalSqlite } from "./db";
|
||||
|
|
@ -14,10 +14,37 @@ export class ReadApi extends Effect.Service<ReadApi>()("ReadApi", {
|
|||
|
||||
yield* Effect.logDebug("--- READ-API ---");
|
||||
|
||||
// TODO: Implémenter ReadApiError pour des requêtes avec zéro retour.
|
||||
|
||||
return {
|
||||
getAllEntries: () => query(_ => _.select().from(DiaryEntries)),
|
||||
getArtworkByTmdbId: (tmdbId: number) =>
|
||||
query(_ =>
|
||||
_.select()
|
||||
.from(ArtWorks)
|
||||
.where(eq(ArtWorks.tmdbId, tmdbId))
|
||||
.limit(1)
|
||||
).pipe(
|
||||
singleResultOrFail(() => new ReadApiError({ cause: "Aucune oeuvre ne dispose de cet ID TMDB." })),
|
||||
),
|
||||
getEntryByTmdbId: (tmdbId: number) =>
|
||||
query(_ =>
|
||||
_.select()
|
||||
.from(DiaryEntries)
|
||||
.leftJoin(ArtWorks, eq(ArtWorks.tmdbId, DiaryEntries.artWorkId))
|
||||
.where(eq(ArtWorks.tmdbId, tmdbId))
|
||||
.limit(1)
|
||||
).pipe(
|
||||
singleResultOrFail(() => new ReadApiError({ cause: "Aucune entrée n'est liée à cet ID TMDB." })),
|
||||
Effect.andThen(result => result.diary_entries),
|
||||
),
|
||||
getLastAddedEntry: () =>
|
||||
query(_ => _.select().from(DiaryEntries).limit(1).orderBy(desc(DiaryEntries.dateCreated))).pipe(
|
||||
query(_ =>
|
||||
_.select()
|
||||
.from(DiaryEntries)
|
||||
.limit(1)
|
||||
.orderBy(desc(DiaryEntries.dateCreated))
|
||||
).pipe(
|
||||
singleResultOrFail(() => new ReadApiError({ cause: "Aucune entrée n'a encore été ajoutée." })),
|
||||
),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
html {
|
||||
box-sizing: border-box;
|
||||
block-size: 100%;
|
||||
text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
tab-size: 2;
|
||||
color-scheme: dark light;
|
||||
interpolate-size: allow-keywords;
|
||||
-moz-text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
block-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
accent-color: var(--color-tertiary);
|
||||
background: var(--color-secondary);
|
||||
block-size: 100%;
|
||||
font-family: monospace, system-ui, sans-serif;
|
||||
font-kerning: normal;
|
||||
font-optical-sizing: auto;
|
||||
font-kerning: normal;
|
||||
font-variant-ligatures: common-ligatures no-discretionary-ligatures no-historical-ligatures
|
||||
contextual;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
line-height: var(--line-height-comfortable);
|
||||
text-decoration-skip-ink: auto;
|
||||
text-rendering: geometricprecision;
|
||||
block-size: 100%;
|
||||
accent-color: var(--color-tertiary);
|
||||
background: var(--color-secondary);
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: inherit;
|
||||
border: 0 solid transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0 solid transparent;
|
||||
font: inherit;
|
||||
font-feature-settings: inherit;
|
||||
font-variation-settings: inherit;
|
||||
color: inherit;
|
||||
letter-spacing: inherit;
|
||||
word-spacing: inherit;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: border-box;
|
||||
}
|
||||
|
||||
:where(.visually-hidden:not(:focus, :active, :focus-within)) {
|
||||
|
|
@ -48,7 +48,7 @@ body {
|
|||
clip-path: inset(50%);
|
||||
}
|
||||
|
||||
:where([hidden]) {
|
||||
:where([hidden]), :where([aria-hidden="true"]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,40 @@
|
|||
/* Élément conteneur de la fenêtre modale. */
|
||||
.dialog {
|
||||
--dialog-overlay-background: var(--bg25-secondary);
|
||||
--dialog-background-color: var(--root-background-color);
|
||||
--dialog-border-color: var(--root-text-color);
|
||||
--dialog-shadow-color: var(--root-text-color);
|
||||
--dialog-heading-font-size: var(--s1);
|
||||
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
margin: 0;
|
||||
background: var(--bg25-secondary);
|
||||
background: var(--dialog-overlay-background);
|
||||
|
||||
&[aria-hidden="true"] {
|
||||
&[aria-hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Fenêtre à proprement parler. */
|
||||
.dialog-content {
|
||||
--layout-box-padding: 0;
|
||||
|
||||
position: relative;
|
||||
margin: auto;
|
||||
border: 1px solid var(--color-primary);
|
||||
background-color: var(--color-secondary);
|
||||
box-shadow: 0.5rem 0.5rem 0 0 var(--color-primary);
|
||||
border: 1px solid var(--dialog-border-color);
|
||||
background-color: var(--dialog-background-color);
|
||||
box-shadow: 0.5rem 0.5rem 0 0 var(--dialog-shadow-color);
|
||||
|
||||
/* Barre d'en-tête. */
|
||||
header {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-block-end: 1px solid var(--color-primary);
|
||||
border-block-end: 1px solid var(--dialog-border-color);
|
||||
|
||||
h2 {
|
||||
padding-inline: var(--s-1);
|
||||
|
|
@ -37,11 +48,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Contenu de la fenêtre. */
|
||||
main {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: start;
|
||||
padding: var(--s0);
|
||||
padding: var(--s1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.box {
|
||||
padding: var(--s0, 1rem);
|
||||
outline: 0.125rem solid transparent; /* 1 */
|
||||
outline-offset: -0.125rem; /* 1 */
|
||||
padding: var(--layout-box-padding);
|
||||
outline: 0.125rem solid transparent;
|
||||
outline-offset: -0.125rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
--brkly-font-weight-regular: 100;
|
||||
--brkly-font-weight-semibold: 120;
|
||||
--banquise-font-weight: 400;
|
||||
|
||||
--root-background-color: var(--color-secondary);
|
||||
--root-text-color: var(--color-primary);
|
||||
--root-font-weight: var(--brkly-font-weight-regular);
|
||||
|
||||
--layout-box-padding: var(--s0);
|
||||
--layout-center-max-width: 80rem;
|
||||
--layout-center-inline-padding: var(--s0);
|
||||
--layout-cluster-gap: var(--s0);
|
||||
|
|
@ -23,8 +22,8 @@
|
|||
|
||||
body {
|
||||
font-family: BRKLY, sans-serif;
|
||||
color: var(--root-text-color);
|
||||
font-weight: var(--root-font-weight);
|
||||
color: var(--root-text-color);
|
||||
background-color: var(--root-background-color);
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +49,9 @@ body {
|
|||
}
|
||||
|
||||
.container {
|
||||
--max-width: 100%;
|
||||
--space: var(--s1);
|
||||
--layout-center-max-width: 100%;
|
||||
--layout-center-inline-padding: var(--s1);
|
||||
--layout-sidebar-last-child-basis: 8rem;
|
||||
|
||||
place-content: start;
|
||||
place-items: start;
|
||||
|
|
@ -64,7 +64,7 @@ body {
|
|||
margin-block-start: var(--s2);
|
||||
}
|
||||
|
||||
main {
|
||||
.container > main {
|
||||
inline-size: 100%;
|
||||
|
||||
> header {
|
||||
|
|
|
|||
|
|
@ -56,11 +56,12 @@ button {
|
|||
/* Bouton sous forme de lien. */
|
||||
&.button-link {
|
||||
--button-border-color: transparent;
|
||||
|
||||
padding-block: var(--s-5);
|
||||
padding-inline: var(--s-4);
|
||||
box-shadow: initial;
|
||||
line-height: var(--line-height-comfortable);
|
||||
outline-offset: initial;
|
||||
box-shadow: initial;
|
||||
|
||||
&:hover {
|
||||
--button-border-color: var(--root-text-color);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
:root {
|
||||
--headings-font-family: Banquise, monospace;
|
||||
--headings-font-family: banquise, monospace;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
a {
|
||||
&[aria-current] {
|
||||
&[aria-current="true"] {
|
||||
font-weight: var(--brkly-font-weight-semibold);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
tsconfig.tsbuildinfo
Normal file
1
tsconfig.tsbuildinfo
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"root":["./src/router/typed-routes.d.ts","./eslint.config.mts","./vite.config.mts","./cfg/drizzle.config.ts","./cfg/eslint-css.config.mts","./cfg/knip.config.ts","./cfg/prettier.config.mjs","./cfg/stylelint.config.mjs","./src/main.ts","./src/vite-env.d.ts","./src/db/schemas.ts","./src/db/schemas/constants.ts","./src/db/schemas/entries.ts","./src/db/schemas/works.ts","./src/libs/apis/clients.ts","./src/libs/apis/requests.ts","./src/libs/apis/routes.ts","./src/libs/apis/tmdb/constants.ts","./src/libs/apis/tmdb/schemas.ts","./src/libs/search/schemas.ts","./src/libs/search/search.ts","./src/libs/types/events.ts","./src/libs/utils/dates.ts","./src/libs/utils/effects.ts","./src/libs/utils/types.d.ts","./src/router/index.ts","./src/services/db.ts","./src/services/logger.ts","./src/services/migrations.ts","./src/services/read-api.ts","./src/services/runtime-client.ts","./src/services/tmdb-api.ts"],"errors":true,"version":"5.7.3"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue