diff --git a/deno.lock b/deno.lock index 3c31c29..2fc24be 100644 --- a/deno.lock +++ b/deno.lock @@ -4,8 +4,10 @@ "npm:eslint-config-next@16.2.3": "16.2.3_eslint@9.39.4_typescript@6.0.2", "npm:eslint@9": "9.39.4", "npm:next@16.2.3": "16.2.3_react@19.2.4_react-dom@19.2.4__react@19.2.4_sass@1.99.0", + "npm:prettier@^3.8.3": "3.8.3", "npm:react-dom@19.2.4": "19.2.4_react@19.2.4", "npm:react-drag-drop-files@^3.1.0": "3.1.0_react@19.2.4_react-dom@19.2.4__react@19.2.4", + "npm:react-markdown@^10.1.0": "10.1.0_@types+react@19.2.14_react@19.2.4", "npm:react@19.2.4": "19.2.4", "npm:sass@^1.99.0": "1.99.0" }, @@ -595,15 +597,54 @@ "tslib" ] }, + "@types/debug@4.1.13": { + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dependencies": [ + "@types/ms" + ] + }, + "@types/estree-jsx@1.0.5": { + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": [ + "@types/estree" + ] + }, "@types/estree@1.0.8": { "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, + "@types/hast@3.0.4": { + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": [ + "@types/unist@3.0.3" + ] + }, "@types/json-schema@7.0.15": { "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/json5@0.0.29": { "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "@types/mdast@4.0.4": { + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": [ + "@types/unist@3.0.3" + ] + }, + "@types/ms@2.1.0": { + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "@types/react@19.2.14": { + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dependencies": [ + "csstype" + ] + }, + "@types/unist@2.0.11": { + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, + "@types/unist@3.0.3": { + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "@typescript-eslint/eslint-plugin@8.58.2_@typescript-eslint+parser@8.58.2__eslint@9.39.4__typescript@6.0.2_eslint@9.39.4_typescript@6.0.2": { "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "dependencies": [ @@ -702,6 +743,9 @@ "eslint-visitor-keys@5.0.1" ] }, + "@ungap/structured-clone@1.3.0": { + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" + }, "@unrs/resolver-binding-android-arm-eabi@1.11.1": { "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", "os": ["android"], @@ -931,6 +975,9 @@ "axobject-query@4.1.0": { "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==" }, + "bail@2.0.2": { + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, "balanced-match@1.0.2": { "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, @@ -1000,6 +1047,9 @@ "caniuse-lite@1.0.30001788": { "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==" }, + "ccount@2.0.1": { + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" + }, "chalk@4.1.2": { "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": [ @@ -1007,6 +1057,18 @@ "supports-color" ] }, + "character-entities-html4@2.1.0": { + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" + }, + "character-entities-legacy@3.0.0": { + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" + }, + "character-entities@2.0.2": { + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, + "character-reference-invalid@2.0.1": { + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==" + }, "chokidar@4.0.3": { "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dependencies": [ @@ -1025,6 +1087,9 @@ "color-name@1.1.4": { "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "comma-separated-tokens@2.0.3": { + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, "concat-map@0.0.1": { "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, @@ -1081,6 +1146,12 @@ "ms" ] }, + "decode-named-character-reference@1.3.0": { + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dependencies": [ + "character-entities" + ] + }, "deep-is@0.1.4": { "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, @@ -1100,9 +1171,18 @@ "object-keys" ] }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, "detect-libc@2.1.2": { "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" }, + "devlop@1.1.0": { + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": [ + "dequal" + ] + }, "doctrine@2.1.0": { "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dependencies": [ @@ -1454,9 +1534,15 @@ "estraverse@5.3.0": { "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, + "estree-util-is-identifier-name@3.0.0": { + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==" + }, "esutils@2.0.3": { "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "extend@3.0.2": { + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "fast-deep-equal@3.1.3": { "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, @@ -1646,6 +1732,32 @@ "function-bind" ] }, + "hast-util-to-jsx-runtime@2.3.6": { + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "dependencies": [ + "@types/estree", + "@types/hast", + "@types/unist@3.0.3", + "comma-separated-tokens", + "devlop", + "estree-util-is-identifier-name", + "hast-util-whitespace", + "mdast-util-mdx-expression", + "mdast-util-mdx-jsx", + "mdast-util-mdxjs-esm", + "property-information", + "space-separated-tokens", + "style-to-js", + "unist-util-position", + "vfile-message" + ] + }, + "hast-util-whitespace@3.0.0": { + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": [ + "@types/hast" + ] + }, "hermes-estree@0.25.1": { "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==" }, @@ -1655,6 +1767,9 @@ "hermes-estree" ] }, + "html-url-attributes@3.0.1": { + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==" + }, "ignore@5.3.2": { "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" }, @@ -1674,6 +1789,9 @@ "imurmurhash@0.1.4": { "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" }, + "inline-style-parser@0.2.7": { + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==" + }, "internal-slot@1.1.0": { "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dependencies": [ @@ -1682,6 +1800,16 @@ "side-channel" ] }, + "is-alphabetical@2.0.1": { + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==" + }, + "is-alphanumerical@2.0.1": { + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": [ + "is-alphabetical", + "is-decimal" + ] + }, "is-array-buffer@3.0.5": { "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dependencies": [ @@ -1743,6 +1871,9 @@ "has-tostringtag" ] }, + "is-decimal@2.0.1": { + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==" + }, "is-extglob@2.1.1": { "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, @@ -1768,6 +1899,9 @@ "is-extglob" ] }, + "is-hexadecimal@2.0.1": { + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==" + }, "is-map@2.0.3": { "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==" }, @@ -1784,6 +1918,9 @@ "is-number@7.0.0": { "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-plain-obj@4.1.0": { + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + }, "is-regex@1.2.1": { "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": [ @@ -1930,6 +2067,9 @@ "lodash.merge@4.6.2": { "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "longest-streak@3.1.0": { + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" + }, "loose-envify@1.4.0": { "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": [ @@ -1946,9 +2086,275 @@ "math-intrinsics@1.1.0": { "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, + "mdast-util-from-markdown@2.0.3": { + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "dependencies": [ + "@types/mdast", + "@types/unist@3.0.3", + "decode-named-character-reference", + "devlop", + "mdast-util-to-string", + "micromark", + "micromark-util-decode-numeric-character-reference", + "micromark-util-decode-string", + "micromark-util-normalize-identifier", + "micromark-util-symbol", + "micromark-util-types", + "unist-util-stringify-position" + ] + }, + "mdast-util-mdx-expression@2.0.1": { + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dependencies": [ + "@types/estree-jsx", + "@types/hast", + "@types/mdast", + "devlop", + "mdast-util-from-markdown", + "mdast-util-to-markdown" + ] + }, + "mdast-util-mdx-jsx@3.2.0": { + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dependencies": [ + "@types/estree-jsx", + "@types/hast", + "@types/mdast", + "@types/unist@3.0.3", + "ccount", + "devlop", + "mdast-util-from-markdown", + "mdast-util-to-markdown", + "parse-entities", + "stringify-entities", + "unist-util-stringify-position", + "vfile-message" + ] + }, + "mdast-util-mdxjs-esm@2.0.1": { + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": [ + "@types/estree-jsx", + "@types/hast", + "@types/mdast", + "devlop", + "mdast-util-from-markdown", + "mdast-util-to-markdown" + ] + }, + "mdast-util-phrasing@4.1.0": { + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": [ + "@types/mdast", + "unist-util-is" + ] + }, + "mdast-util-to-hast@13.2.1": { + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dependencies": [ + "@types/hast", + "@types/mdast", + "@ungap/structured-clone", + "devlop", + "micromark-util-sanitize-uri", + "trim-lines", + "unist-util-position", + "unist-util-visit", + "vfile" + ] + }, + "mdast-util-to-markdown@2.1.2": { + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dependencies": [ + "@types/mdast", + "@types/unist@3.0.3", + "longest-streak", + "mdast-util-phrasing", + "mdast-util-to-string", + "micromark-util-classify-character", + "micromark-util-decode-string", + "unist-util-visit", + "zwitch" + ] + }, + "mdast-util-to-string@4.0.0": { + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": [ + "@types/mdast" + ] + }, "merge2@1.4.1": { "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, + "micromark-core-commonmark@2.0.3": { + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dependencies": [ + "decode-named-character-reference", + "devlop", + "micromark-factory-destination", + "micromark-factory-label", + "micromark-factory-space", + "micromark-factory-title", + "micromark-factory-whitespace", + "micromark-util-character", + "micromark-util-chunked", + "micromark-util-classify-character", + "micromark-util-html-tag-name", + "micromark-util-normalize-identifier", + "micromark-util-resolve-all", + "micromark-util-subtokenize", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-destination@2.0.1": { + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dependencies": [ + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-label@2.0.1": { + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dependencies": [ + "devlop", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-space@2.0.1": { + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dependencies": [ + "micromark-util-character", + "micromark-util-types" + ] + }, + "micromark-factory-title@2.0.1": { + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dependencies": [ + "micromark-factory-space", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-whitespace@2.0.1": { + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dependencies": [ + "micromark-factory-space", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-character@2.1.1": { + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dependencies": [ + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-chunked@2.0.1": { + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-classify-character@2.0.1": { + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dependencies": [ + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-combine-extensions@2.0.1": { + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dependencies": [ + "micromark-util-chunked", + "micromark-util-types" + ] + }, + "micromark-util-decode-numeric-character-reference@2.0.2": { + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-decode-string@2.0.1": { + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dependencies": [ + "decode-named-character-reference", + "micromark-util-character", + "micromark-util-decode-numeric-character-reference", + "micromark-util-symbol" + ] + }, + "micromark-util-encode@2.0.1": { + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==" + }, + "micromark-util-html-tag-name@2.0.1": { + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==" + }, + "micromark-util-normalize-identifier@2.0.1": { + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-resolve-all@2.0.1": { + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dependencies": [ + "micromark-util-types" + ] + }, + "micromark-util-sanitize-uri@2.0.1": { + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dependencies": [ + "micromark-util-character", + "micromark-util-encode", + "micromark-util-symbol" + ] + }, + "micromark-util-subtokenize@2.1.0": { + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dependencies": [ + "devlop", + "micromark-util-chunked", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-symbol@2.0.1": { + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==" + }, + "micromark-util-types@2.0.2": { + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==" + }, + "micromark@4.0.2": { + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dependencies": [ + "@types/debug", + "debug@4.4.3", + "decode-named-character-reference", + "devlop", + "micromark-core-commonmark", + "micromark-factory-space", + "micromark-util-character", + "micromark-util-chunked", + "micromark-util-combine-extensions", + "micromark-util-decode-numeric-character-reference", + "micromark-util-encode", + "micromark-util-normalize-identifier", + "micromark-util-resolve-all", + "micromark-util-sanitize-uri", + "micromark-util-subtokenize", + "micromark-util-symbol", + "micromark-util-types" + ] + }, "micromatch@4.0.8": { "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": [ @@ -2121,6 +2527,18 @@ "callsites" ] }, + "parse-entities@4.0.2": { + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dependencies": [ + "@types/unist@2.0.11", + "character-entities-legacy", + "character-reference-invalid", + "decode-named-character-reference", + "is-alphanumerical", + "is-decimal", + "is-hexadecimal" + ] + }, "path-exists@4.0.0": { "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, @@ -2153,6 +2571,10 @@ "prelude-ls@1.2.1": { "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier@3.8.3": { + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "bin": true + }, "prop-types@15.8.1": { "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": [ @@ -2161,6 +2583,9 @@ "react-is" ] }, + "property-information@7.1.0": { + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==" + }, "punycode@2.3.1": { "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, @@ -2186,6 +2611,24 @@ "react-is@16.13.1": { "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-markdown@10.1.0_@types+react@19.2.14_react@19.2.4": { + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "dependencies": [ + "@types/hast", + "@types/mdast", + "@types/react", + "devlop", + "hast-util-to-jsx-runtime", + "html-url-attributes", + "mdast-util-to-hast", + "react", + "remark-parse", + "remark-rehype", + "unified", + "unist-util-visit", + "vfile" + ] + }, "react@19.2.4": { "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==" }, @@ -2216,6 +2659,25 @@ "set-function-name" ] }, + "remark-parse@11.0.0": { + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": [ + "@types/mdast", + "mdast-util-from-markdown", + "micromark-util-types", + "unified" + ] + }, + "remark-rehype@11.1.2": { + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "dependencies": [ + "@types/hast", + "@types/mdast", + "mdast-util-to-hast", + "unified", + "vfile" + ] + }, "resolve-from@4.0.0": { "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, @@ -2402,6 +2864,9 @@ "source-map-js@1.2.1": { "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, + "space-separated-tokens@2.0.2": { + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, "stable-hash@0.0.5": { "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==" }, @@ -2474,12 +2939,31 @@ "es-object-atoms" ] }, + "stringify-entities@4.0.4": { + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": [ + "character-entities-html4", + "character-entities-legacy" + ] + }, "strip-bom@3.0.0": { "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" }, "strip-json-comments@3.1.1": { "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "style-to-js@1.1.21": { + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "dependencies": [ + "style-to-object" + ] + }, + "style-to-object@1.0.14": { + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dependencies": [ + "inline-style-parser" + ] + }, "styled-components@6.4.0_react@19.2.4_react-dom@19.2.4__react@19.2.4": { "integrity": "sha512-BL1EDFpt+q10eAeZB0q9ps6pSlPejaBQWBkiuM16pyoVTG4NhZrPrZK0cqNbrozxSsYwUsJ9SQYN6NyeKJYX9A==", "dependencies": [ @@ -2525,6 +3009,12 @@ "is-number" ] }, + "trim-lines@3.0.1": { + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, + "trough@2.2.0": { + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" + }, "ts-api-utils@2.5.0_typescript@6.0.2": { "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dependencies": [ @@ -2614,6 +3104,51 @@ "which-boxed-primitive" ] }, + "unified@11.0.5": { + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": [ + "@types/unist@3.0.3", + "bail", + "devlop", + "extend", + "is-plain-obj", + "trough", + "vfile" + ] + }, + "unist-util-is@6.0.1": { + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dependencies": [ + "@types/unist@3.0.3" + ] + }, + "unist-util-position@5.0.0": { + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": [ + "@types/unist@3.0.3" + ] + }, + "unist-util-stringify-position@4.0.0": { + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": [ + "@types/unist@3.0.3" + ] + }, + "unist-util-visit-parents@6.0.2": { + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dependencies": [ + "@types/unist@3.0.3", + "unist-util-is" + ] + }, + "unist-util-visit@5.1.0": { + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dependencies": [ + "@types/unist@3.0.3", + "unist-util-is", + "unist-util-visit-parents" + ] + }, "unrs-resolver@1.11.1": { "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dependencies": [ @@ -2657,6 +3192,20 @@ "punycode" ] }, + "vfile-message@4.0.3": { + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dependencies": [ + "@types/unist@3.0.3", + "unist-util-stringify-position" + ] + }, + "vfile@6.0.3": { + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": [ + "@types/unist@3.0.3", + "vfile-message" + ] + }, "which-boxed-primitive@1.1.1": { "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dependencies": [ @@ -2730,6 +3279,9 @@ }, "zod@4.3.6": { "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==" + }, + "zwitch@2.0.4": { + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" } }, "workspace": { @@ -2738,8 +3290,10 @@ "npm:eslint-config-next@16.2.3", "npm:eslint@9", "npm:next@16.2.3", + "npm:prettier@^3.8.3", "npm:react-dom@19.2.4", "npm:react-drag-drop-files@^3.1.0", + "npm:react-markdown@^10.1.0", "npm:react@19.2.4", "npm:sass@^1.99.0" ] diff --git a/next.config.mjs b/next.config.mjs index b108e1a..aace8fe 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - /* config options here */ + allowedDevOrigins: ['192.168.11.170'], }; export default nextConfig; diff --git a/package.json b/package.json index 3066e8d..c4b82fa 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,11 @@ }, "dependencies": { "next": "16.2.3", + "prettier": "^3.8.3", "react": "19.2.4", "react-dom": "19.2.4", "react-drag-drop-files": "^3.1.0", + "react-markdown": "^10.1.0", "sass": "^1.99.0" }, "devDependencies": { diff --git a/public/uploads/3/16/CV_Marsha_Walungua.jpg b/public/uploads/3/16/CV_Marsha_Walungua.jpg new file mode 100644 index 0000000..17d0339 Binary files /dev/null and b/public/uploads/3/16/CV_Marsha_Walungua.jpg differ diff --git a/public/uploads/3/16/hello.jpg b/public/uploads/3/16/hello.jpg new file mode 100644 index 0000000..05dd542 Binary files /dev/null and b/public/uploads/3/16/hello.jpg differ diff --git a/public/uploads/3/16/note.md b/public/uploads/3/16/note.md new file mode 100644 index 0000000..ab7f3c7 --- /dev/null +++ b/public/uploads/3/16/note.md @@ -0,0 +1,14 @@ + +## Entry - 8:23:28 PM + +**Rating:** 3/5 + +this is a test + +## Entry - 10:05:53 PM + +**Rating:** 5/5 + +hello + +--- diff --git a/src/app/Component/preview.jsx b/src/app/Component/preview.jsx index b053fd3..78032ac 100644 --- a/src/app/Component/preview.jsx +++ b/src/app/Component/preview.jsx @@ -1,12 +1,37 @@ -import { useEffect } from "react" +import { useEffect, useState } from "react" +import Markdown from "react-markdown" export default function Preview({daySelected}){ + const [preview,setPreview] = useState(undefined) + + function fetchPreview(){ + fetch(`/api/singleday?date=${encodeURIComponent(daySelected)}`).then((res)=>res.json()).then(data=>{setPreview(data)}) + } + useEffect(()=>{ - fetch(`/api/days`) + fetchPreview() + },[daySelected]) + + useEffect(()=>{ + fetchPreview() },[]) + + + if (!preview || preview.files?.length === 0) { + return

nothing for this date

; + } + return( -
- preview +
+ {preview?.files?.map(((el,i)=>( +
+ +
+ + )))} +
+ {preview?.note || ""} +
) } \ No newline at end of file diff --git a/src/app/Component/tracker.jsx b/src/app/Component/tracker.jsx index e87db9f..b6d9d3c 100644 --- a/src/app/Component/tracker.jsx +++ b/src/app/Component/tracker.jsx @@ -1,31 +1,53 @@ -import { useEffect } from "react" +import { useEffect, useState } from "react"; -export default function Tracker({calendar,setDaySelected}){ - - useEffect(()=>{ - fetch(`/api/days`) - },[]) - const monthLine = Object.keys(calendar).map((month, monthIndex) => -
-

- {month.charAt(0).toUpperCase() + month.slice(1)} -

-
- {[...Array(calendar[month])].map( - (dayValue, dayIndex) => ( - {setDaySelected(`${monthIndex+1}/${dayIndex+1}`)}} id={dayIndex} key={dayIndex} title={`${dayIndex+1} ${month}`} className="day"/> - ) - )} -
-
- ) +export default function Tracker({ calendar, setDaySelected }) { + const [existingDays, setExistingDays] = useState([]); - + useEffect(() => { + fetch(`/api/days`) + .then(res => res.json()) + .then(data => setExistingDays(data)) + .then(console.log(existingDays)) + }, []); - return( -
- {monthLine} -
- ) -}; \ No newline at end of file + + function getColor(rating) { + const colors = [ + "#d6e6854c", // 0 + "#d6e685", // 1 + "#8cc665", // 2 + "#44a340", // 3 + "#1e6823", // 4 + "#0f3d1a" // 5 + ]; + + return colors[rating] || "#eee"; + } + + const monthLine = Object.keys(calendar).map((month, monthIndex) => ( +
+

{month.charAt(0).toUpperCase() + month.slice(1)}

+ +
+ {[...Array(calendar[month])].map((_, dayIndex) => { + const dayString = `${monthIndex + 1}/${dayIndex + 1}`; + const dayData = existingDays.find(d => d.date === dayString); + const rating = dayData?.appreciation ?? 0; + + return ( + setDaySelected(dayString)} + title={`${dayIndex + 1} ${month} (${rating}/5)`} + className="day" + style={{ backgroundColor: getColor(rating) }} + /> + ); + })} +
+
+ )); + + return
{monthLine}
; +} \ No newline at end of file diff --git a/src/app/Component/upload.jsx b/src/app/Component/upload.jsx index c2fac49..439be65 100644 --- a/src/app/Component/upload.jsx +++ b/src/app/Component/upload.jsx @@ -24,14 +24,18 @@ function DragDrop({setPreview ,file, setFile}) { } -export default function Upload({daySelected={daySelected}}){ +export default function Upload({setFetchKey, daySelected}){ const [file, setFile] = useState(null); const [preview, setPreview] = useState(null); + const [note,setNote] = useState(undefined) + const [rating,setRating] = useState(1) function uploadFile(){ const formData = new FormData(); formData.append("file", file); formData.append("date", daySelected) + formData.append("note", note) + formData.append("rating", rating) fetch("/api/days", { method: "POST", @@ -39,6 +43,9 @@ export default function Upload({daySelected={daySelected}}){ }).then(() => { setFile(null); setPreview(null); + setNote(undefined); + setRating(1); + setFetchKey(Math.random()*10) }); } @@ -51,12 +58,19 @@ export default function Upload({daySelected={daySelected}}){ - - {setRating(e.target.value)}}> + + + + + + +

{ uploadFile() }}>Upload

diff --git a/src/app/api/days/[id]/route.js b/src/app/api/days/[id]/route.js deleted file mode 100644 index 3cfaecf..0000000 --- a/src/app/api/days/[id]/route.js +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET(req){ - console.log(req) - return Response.json({"message":"hello"}) - -} \ No newline at end of file diff --git a/src/app/api/days/route.js b/src/app/api/days/route.js index 0e20eca..3eea02c 100644 --- a/src/app/api/days/route.js +++ b/src/app/api/days/route.js @@ -1,42 +1,105 @@ -import {readdir, access, mkdir, writeFile } from "fs/promises"; +import {readdir,appendFile, readFile, mkdir, writeFile } from "fs/promises"; import path from "path"; -export async function GET(req) { - const dir = "./public/uploads"; - const entries = await readdir(dir, { withFileTypes: true }); - console.log(entries) +export async function GET() { + const baseDir = "./public/uploads"; + const results = []; + try { + const months = await readdir(baseDir, { withFileTypes: true }); - return Response.json({"hello":"wesh"}) + for (const month of months) { + if (!month.isDirectory()) continue; + + const monthPath = path.join(baseDir, month.name); + const days = await readdir(monthPath, { withFileTypes: true }); + + for (const day of days) { + if (!day.isDirectory()) continue; + + const date = `${month.name}/${day.name}`; + const mdPath = path.join(monthPath, day.name, "note.md"); + + let appreciation = null; + + try { + const content = await readFile(mdPath, "utf-8"); + + // ๐ŸŽฃ extract all ratings from markdown + const matches = [ + ...content.matchAll(/\*\*Rating:\*\*\s*(\d+)\/5/g), + ]; + + const ratings = matches.map(m => Number(m[1])); + + if (ratings.length > 0) { + // ๐Ÿ“Š average + round to 1 decimal + appreciation = + Math.round( + (ratings.reduce((a, b) => a + b, 0) / ratings.length) * 10 + ) / 10; + } + + } catch { + // no note.md โ†’ keep appreciation = null + } + + results.push({ date, appreciation }); + } + } + + // ๐Ÿ“… sort by month/day + results.sort((a, b) => { + const [m1, d1] = a.date.split("/").map(Number); + const [m2, d2] = b.date.split("/").map(Number); + + return m1 - m2 || d1 - d2; + }); + + return Response.json(results); + + } catch (err) { + return Response.json( + { error: "Failed to read uploads directory" }, + { status: 500 } + ); + } } export async function POST(req) { const formData = await req.formData(); - const date = formData.get('date'); - const file = formData.get('file'); - console.log(file) - if (!file) { - return new Response("No file uploaded", { status: 400 }); - } + const file = formData.get("file"); + const date = formData.get("date"); + const note = formData.get("note"); + const rating = formData.get("rating"); + + const dir = `./public/uploads/${date}`; + await mkdir(dir, { recursive: true }); + + // ๐Ÿ“ Save file const bytes = await file.arrayBuffer(); const buffer = Buffer.from(bytes); + const filePath = path.join(dir, file.name); + await writeFile(filePath, buffer); - // if folder not created make one - try{ - await access(`./public/uploads/${date}/`) - }catch{ - await mkdir(`./public/uploads/${date}/`, {recursive:true}) - } + // ๐Ÿงผ sanitize + const safeNote = note?.toString().trim() || "No note"; + const safeRating = Math.max(0, Math.min(5, Number(rating) || 0)); - //get the number of files before uploading - const files = await readdir(`./public/uploads/${date}/`); - const nbFiles = (files.length); - const fileName = `${nbFiles}.${path.extname(file.name).slice(1).toLowerCase()}` - console.log(fileName); - // Save file locally - const pathName = `./public/uploads/${date}/${fileName}`; + // ๐Ÿ“ Append to markdown + const mdEntry = ` +## Entry - ${new Date().toLocaleTimeString()} - await writeFile(pathName, buffer); - return new Response("File uploaded successfully"); +**Rating:** ${safeRating}/5 + +${safeNote} + +--- +`; + + const mdPath = path.join(dir, "note.md"); + await appendFile(mdPath, mdEntry); + + return new Response("Saved"); } \ No newline at end of file diff --git a/src/app/api/links/route.js b/src/app/api/links/route.js new file mode 100644 index 0000000..5fac97b --- /dev/null +++ b/src/app/api/links/route.js @@ -0,0 +1,75 @@ +import {readFile, writeFile, appendFile, mkdir } from "fs/promises"; +import path from "path"; + +export async function GET() { + const filePath = "./public/links/links.md"; + + try { + const content = await readFile(filePath, "utf-8"); + + const entries = content.split("---").map(block => { + const titleMatch = block.match(/\[(.*?)\]\((.*?)\)/); + const ratingMatch = block.match(/Rating:\s*(\d+)/); + const noteMatch = block.match(/Note:\s*(.*)/); + + if (!titleMatch) return null; + + return { + title: titleMatch[1], + url: titleMatch[2], + rating: ratingMatch ? Number(ratingMatch[1]) : null, + note: noteMatch ? noteMatch[1] : "", + }; + }).filter(Boolean); + + return Response.json(entries); + + } catch { + return Response.json([]); + } +} + +export async function POST(req) { + const { title, url, note, rating } = await req.json(); + + const dir = "./public/links"; + const filePath = path.join(dir, "links.md"); + + await mkdir(dir, { recursive: true }); + + const safeRating = Math.max(0, Math.min(5, Number(rating) || 0)); + + const entry = ` +## [${title}](${url}) +Rating: ${safeRating} +Note: ${note || ""} + +--- +`; + + await appendFile(filePath, entry); + + return Response.json({ success: true }); +} + + +export async function DELETE(req) { + const { url } = await req.json(); + + const filePath = "./public/links/links.md"; + + try { + const content = await readFile(filePath, "utf-8"); + + const blocks = content.split("---"); + + const filtered = blocks.filter(block => !block.includes(`(${url})`)); + + await writeFile(filePath, filtered.join("---")); + + return Response.json({ success: true }); + + } catch { + return Response.json({ error: "Failed to delete" }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/singleday/route.js b/src/app/api/singleday/route.js new file mode 100644 index 0000000..ec32075 --- /dev/null +++ b/src/app/api/singleday/route.js @@ -0,0 +1,36 @@ +import { readdir, readFile } from "fs/promises"; +import path from "path"; + +export async function GET(req) { + const { searchParams } = new URL(req.url); + const date = searchParams.get("date"); + + const dir = `./public/uploads/${date}`; + + try { + // ๐Ÿ“‚ Get files + const entries = await readdir(dir, { withFileTypes: true }); + + const files = entries + .filter(e => e.isFile() && e.name !== "note.md") + .map(e => e.name); + + // ๐Ÿ“– Get markdown content + let note = ""; + try { + const mdPath = path.join(dir, "note.md"); + note = await readFile(mdPath, "utf-8"); + } catch { + note = ""; + } + + return Response.json({ + date, + files, + note, + }); + + } catch (err) { + return Response.json({ error: "Not found" }, { status: 404 }); + } +} \ No newline at end of file diff --git a/src/app/page.js b/src/app/page.js index 89361dc..820b83b 100644 --- a/src/app/page.js +++ b/src/app/page.js @@ -5,6 +5,7 @@ import "./style.sass" import Preview from "./Component/preview"; import Upload from "./Component/upload"; import Tracker from "./Component/tracker"; + export default function Home() { const calendar = { @@ -25,22 +26,23 @@ export default function Home() { const date = new Date(); const [daySelected, setDaySelected] = useState(`${date.getMonth()}/${date.getDate()}`) + const [fetchKey,setFetchKey] = useState(Math.random*10) return ( -
+
-

{daySelected}

- -
+ +
); } diff --git a/src/app/style.sass b/src/app/style.sass index 31fb0b5..5dbce46 100644 --- a/src/app/style.sass +++ b/src/app/style.sass @@ -5,8 +5,7 @@ justify-content: space-between height: 100vh aside - background: rebeccapurple - width: 20% + width: 30% height: 100% main width: 79% @@ -16,28 +15,39 @@ main .month display: flex flex-direction: row + flex-wrap: wrap + width: 100% align-items: center gap: 5px p - width: 8% + width: 20% .monthBlock display: flex - gap: 5px - width: 90% + gap: 1px .day display: block - width: 20px + width: 10px height: 10px - background: green - border-radius: 5px cursor: pointer + .Upload display: flex gap: 5% align-items: center + flex-wrap: wrap .preview - height: 400px + height: 100px img height: 100% select - height: 40px \ No newline at end of file + height: 40px + +.Preview + display: flex + flex-wrap: wrap + gap : 5% + .imagePreview + height: 500px + img + max-height: 100% + object-fit: contain \ No newline at end of file