diff --git a/.dockerignore b/.dockerignore index c8ef8b6..f40e224 100644 --- a/.dockerignore +++ b/.dockerignore @@ -47,9 +47,6 @@ Dockerfile* docker-compose*.yml .dockerignore -# Frontend old -frontend-old/ - # Build artifacts public/build/ public/hot diff --git a/.gitignore b/.gitignore index a9a82cb..ceebbb0 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ yarn-error.log /.vscode /.zed /.vite +/.codewhale diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c98d309 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.7.0] - 2026-08-17 + +### Removed + +- **SaaS mode and subscription billing** (#37) — dropped Laravel Cashier, the `Billable` trait, `SubscriptionController`, the `RequireSubscription`/`RequireSaasMode` middleware, the `/subscription` and `/billing` routes, the billing/subscription views, the Cashier migrations, and the `stripe` service config. +- **Demo mode** (#38) — removed the demo middleware, demo-account seeding and purge commands, and demo-only UI. +- **`APP_MODE` plumbing** (#39) — removed `AppModeEnum`, the `app/helpers.php` global helper, and the `mode`/`demo_subscribe_url` config keys; the app now runs in a single self-hosted mode. +- **Dead code** (#40) — deleted `frontend-old/`, `bin/start-dev`, the unused `welcome.blade.php`, and the stale root-level `build_and_push.sh`/`update.sh` scripts. + +### Changed + +- **License** (#41) — adopted AGPL-3.0 as the single project license (`LICENSE.md`, `composer.json`, `README.md`). +- **Registry and remote references** — repointed build, push, and update scripts and docs at `forge.lvl0.xyz/lvl0/dishplanner`, removing the stale LAN-registry and old `backend` naming. +- **Documentation** — updated the production and development instructions. + +### Fixed + +- Fixed health-check warnings in the development shell. +- Fixed schedule regeneration iterating every user instead of the planner's own users. diff --git a/LICENSE.md b/LICENSE.md index 6b111d1..be3f7b2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,106 +1,94 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 -Copyright (C) 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. Preamble -The GNU General Public License is a free, copyleft license for -software and other kinds of works. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. -The licenses for most software and other practical works are designed + The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to +our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. +software for all its users. -When we speak of free software, we are referring to freedom, not + When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. -Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. -For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. -Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - -Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and + The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS -0. Definitions. + 0. Definitions. -"This License" refers to version 3 of the GNU General Public License. + "This License" refers to version 3 of the GNU Affero General Public License. -"Copyright" also means copyright-like laws that apply to other kinds of + "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. -"The Program" refers to any copyrightable work licensed under this + "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. -To "modify" a work means to copy from or adapt all or part of the work + To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. -A "covered work" means either the unmodified Program or a work based + A "covered work" means either the unmodified Program or a work based on the Program. -To "propagate" a work means to do anything with it that, without + To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. -To "convey" a work means any kind of propagation that enables other + To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. -An interactive user interface displays "Appropriate Legal Notices" + An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the @@ -109,18 +97,18 @@ the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. -1. Source Code. + 1. Source Code. -The "source code" for a work means the preferred form of the work + The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. -A "Standard Interface" means an interface that either is an official + A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. -The "System Libraries" of an executable work include anything, other + The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that @@ -131,7 +119,7 @@ (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. -The "Corresponding Source" for a work in object code form means all + The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's @@ -144,16 +132,16 @@ such as by intimate data communication or control flow between those subprograms and other parts of the work. -The Corresponding Source need not include anything that users + The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. -The Corresponding Source for a work in source code form is that + The Corresponding Source for a work in source code form is that same work. -2. Basic Permissions. + 2. Basic Permissions. -All rights granted under this License are granted for the term of + All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a @@ -161,7 +149,7 @@ content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. -You may make, run and propagate covered works that you do not + You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you @@ -172,19 +160,19 @@ and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. -Conveying under any other circumstances is permitted solely under + Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. -3. Protecting Users' Legal Rights From Anti-Circumvention Law. + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. -No covered work shall be deemed part of an effective technological + No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. -When you convey a covered work, you waive any legal power to forbid + When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or @@ -192,9 +180,9 @@ users, your or third parties' legal rights to forbid circumvention of technological measures. -4. Conveying Verbatim Copies. + 4. Conveying Verbatim Copies. -You may convey verbatim copies of the Program's source code as you + You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any @@ -202,12 +190,12 @@ keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. -You may charge any price or no price for each copy that you convey, + You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. -5. Conveying Modified Source Versions. + 5. Conveying Modified Source Versions. -You may convey a work based on the Program, or the modifications to + You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: @@ -232,7 +220,7 @@ interfaces that do not display Appropriate Legal Notices, your work need not make them do so. -A compilation of a covered work with other separate and independent + A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an @@ -242,9 +230,9 @@ in an aggregate does not cause this License to apply to the other parts of the aggregate. -6. Conveying Non-Source Forms. + 6. Conveying Non-Source Forms. -You may convey a covered work in object code form under the terms + You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: @@ -290,11 +278,11 @@ Source of the work are being offered to the general public at no charge under subsection 6d. -A separable portion of the object code, whose source code is excluded + A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. -A "User Product" is either (1) a "consumer product", which means any + A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, @@ -307,7 +295,7 @@ commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. -"Installation Information" for a User Product means any methods, + "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must @@ -315,7 +303,7 @@ code is in no case prevented or interfered with solely because modification has been made. -If you convey an object code work under this section in, or with, or + If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a @@ -326,7 +314,7 @@ modified object code on the User Product (for example, the work has been installed in ROM). -The requirement to provide Installation Information does not include a + The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a @@ -334,15 +322,15 @@ adversely affects the operation of the network or violates the rules and protocols for communication across the network. -Corresponding Source conveyed, and Installation Information provided, + Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. -7. Additional Terms. + 7. Additional Terms. -"Additional permissions" are terms that supplement the terms of this + "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent @@ -351,14 +339,14 @@ under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. -When you convey a copy of a covered work, you may at your option + When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. -Notwithstanding any other provision of this License, for material you + Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: @@ -385,7 +373,7 @@ any liability that these contractual assumptions directly impose on those licensors and authors. -All other non-permissive additional terms are considered "further + All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further @@ -395,46 +383,46 @@ of that license document, provided that the further restriction does not survive such relicensing or conveying. -If you add terms to a covered work in accord with this section, you + If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. -Additional terms, permissive or non-permissive, may be stated in the + Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. -8. Termination. + 8. Termination. -You may not propagate or modify a covered work except as expressly + You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). -However, if you cease all violation of this License, then your + However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. -Moreover, your license from a particular copyright holder is + Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. -Termination of your rights under this section does not terminate the + Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. -9. Acceptance Not Required for Having Copies. + 9. Acceptance Not Required for Having Copies. -You are not required to accept this License in order to receive or + You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, @@ -443,14 +431,14 @@ not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. -10. Automatic Licensing of Downstream Recipients. + 10. Automatic Licensing of Downstream Recipients. -Each time you convey a covered work, the recipient automatically + Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. -An "entity transaction" is a transaction transferring control of an + An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that @@ -460,7 +448,7 @@ Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. -You may not impose any further restrictions on the exercise of the + You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation @@ -468,13 +456,13 @@ any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. -11. Patents. + 11. Patents. -A "contributor" is a copyright holder who authorizes use under this + A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". -A contributor's "essential patent claims" are all patent claims + A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, @@ -484,19 +472,19 @@ patent sublicenses in a manner consistent with the requirements of this License. -Each contributor grants you a non-exclusive, worldwide, royalty-free + Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. -In the following three paragraphs, a "patent license" is any express + In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. -If you convey a covered work, knowingly relying on a patent license, + If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, @@ -510,7 +498,7 @@ in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. -If, pursuant to or in connection with a single transaction or + If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify @@ -518,7 +506,7 @@ you grant is automatically extended to all recipients of the covered work and works based on it. -A patent license is "discriminatory" if it does not include within + A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered @@ -533,13 +521,13 @@ contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. -Nothing in this License shall be construed as excluding or limiting + Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. -12. No Surrender of Others' Freedom. + 12. No Surrender of Others' Freedom. -If conditions are imposed on you (whether by court order, agreement or + If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this @@ -549,46 +537,56 @@ the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. -13. Use with the GNU Affero General Public License. + 13. Remote Network Interaction; Use with the GNU General Public License. -Notwithstanding any other provision of this License, you have + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single +under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. -14. Revised Versions of this License. + 14. Revised Versions of this License. -The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published +GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. -If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. -Later license versions may give you additional or different + Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. -15. Disclaimer of Warranty. + 15. Disclaimer of Warranty. -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, @@ -597,9 +595,9 @@ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -16. Limitation of Liability. + 16. Limitation of Liability. -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE @@ -609,9 +607,9 @@ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -17. Interpretation of Sections 15 and 16. + 17. Interpretation of Sections 15 and 16. -If the disclaimer of warranty and limitation of liability provided + If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the @@ -622,11 +620,11 @@ How to Apply These Terms to Your New Programs -If you develop a new program, and you want it to be of the greatest + If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. -To do so, attach the following notices to the program. It is safest + To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. @@ -635,40 +633,29 @@ Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by + it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. -If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - -You should also get your employer (if you work as a programmer) or school, + You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see +For more information on this, and how to apply and follow the GNU AGPL, see . - -The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/Makefile b/Makefile deleted file mode 100644 index 01617f9..0000000 --- a/Makefile +++ /dev/null @@ -1,123 +0,0 @@ -# Dish Planner - Docker Commands - -.PHONY: help -help: ## Show this help message - @echo "Dish Planner - Docker Management" - @echo "" - @echo "Usage: make [command]" - @echo "" - @echo "Available commands:" - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}' - -# Development Commands -.PHONY: dev -dev: ## Start development environment - docker compose up -d - @echo "Development server running at http://localhost:8000" - @echo "Mailhog available at http://localhost:8025" - -.PHONY: dev-build -dev-build: ## Build and start development environment - docker compose build - docker compose up -d - -.PHONY: dev-stop -dev-stop: ## Stop development environment - docker compose down - -.PHONY: dev-clean -dev-clean: ## Stop and remove volumes (CAUTION: removes database) - docker compose down -v - -.PHONY: logs -logs: ## Show application logs - docker compose logs -f app - -.PHONY: logs-db -logs-db: ## Show database logs - docker compose logs -f db - -# Production Commands -.PHONY: prod-build -prod-build: ## Build production image for Codeberg - ./bin/build-push.sh - -.PHONY: prod-build-tag -prod-build-tag: ## Build with specific tag (usage: make prod-build-tag TAG=v1.0.0) - ./bin/build-push.sh $(TAG) - -.PHONY: prod-login -prod-login: ## Login to Codeberg registry - podman login codeberg.org - -# Laravel Commands -.PHONY: artisan -artisan: ## Run artisan command (usage: make artisan cmd="migrate") - docker compose exec app php artisan $(cmd) - -.PHONY: composer -composer: ## Run composer command (usage: make composer cmd="require package") - docker compose exec app composer $(cmd) - -.PHONY: npm -npm: ## Run npm command (usage: make npm cmd="install package") - docker compose exec app npm $(cmd) - -.PHONY: migrate -migrate: ## Run database migrations - docker compose exec app php artisan migrate - -.PHONY: seed -seed: ## Seed the database - docker compose exec app php artisan db:seed - -.PHONY: fresh -fresh: ## Fresh migrate and seed - docker compose exec app php artisan migrate:fresh --seed - -.PHONY: tinker -tinker: ## Start Laravel tinker - docker compose exec app php artisan tinker - -.PHONY: test -test: ## Run tests - docker compose exec app php artisan test - -# Utility Commands -.PHONY: shell -shell: ## Enter app container shell - docker compose exec app sh - -.PHONY: db-shell -db-shell: ## Enter database shell - docker compose exec db mariadb -u dishplanner -pdishplanner dishplanner - -.PHONY: clear -clear: ## Clear all Laravel caches - docker compose exec app php artisan cache:clear - docker compose exec app php artisan config:clear - docker compose exec app php artisan route:clear - docker compose exec app php artisan view:clear - -.PHONY: optimize -optimize: ## Optimize Laravel for production - docker compose exec app php artisan config:cache - docker compose exec app php artisan route:cache - docker compose exec app php artisan view:cache - docker compose exec app php artisan livewire:discover - -# Installation -.PHONY: install -install: ## First time setup - @echo "Setting up Dish Planner..." - @cp -n .env.example .env || true - @echo "Generating application key..." - @docker compose build - @docker compose up -d - @sleep 5 - @docker compose exec app php artisan key:generate - @docker compose exec app php artisan migrate - @echo "" - @echo "✅ Installation complete!" - @echo "Access the app at: http://localhost:8000" - @echo "Create your first planner and user to get started." \ No newline at end of file diff --git a/README.md b/README.md index ea4cdbd..a233b72 100644 --- a/README.md +++ b/README.md @@ -11,285 +11,118 @@ ## ✨ Features - **Dark theme UI** - Modern interface with purple/pink accents - **Single container deployment** - Simplified hosting with FrankenPHP -## 🚀 Quick Start +## 🚀 Self-hosting -### Prerequisites -- Docker and Docker Compose -- Make (optional, for convenience commands) +The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`. -### First Time Setup +### docker-compose.yml -```bash -# Clone the repository -git clone https://github.com/yourusername/dish-planner.git -cd dish-planner +```yaml +services: + app: + image: forge.lvl0.xyz/lvl0/dishplanner:latest + container_name: dishplanner_app + restart: always + ports: + - "8000:8000" + environment: + APP_KEY: "${APP_KEY}" + APP_URL: "${APP_URL}" + DB_DATABASE: "${DB_DATABASE}" + DB_USERNAME: "${DB_USERNAME}" + DB_PASSWORD: "${DB_PASSWORD}" + MAIL_HOST: "${MAIL_HOST:-}" + MAIL_PORT: "${MAIL_PORT:-587}" + MAIL_USERNAME: "${MAIL_USERNAME:-}" + MAIL_PASSWORD: "${MAIL_PASSWORD:-}" + MAIL_FROM_ADDRESS: "${MAIL_FROM_ADDRESS:-noreply@example.com}" + volumes: + - app_storage:/app/storage + depends_on: + - db + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/up"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s -# Quick install with Make -make install + db: + image: mariadb:11 + container_name: dishplanner_db + restart: always + environment: + MYSQL_DATABASE: "${DB_DATABASE}" + MYSQL_USER: "${DB_USERNAME}" + MYSQL_PASSWORD: "${DB_PASSWORD}" + MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}" + volumes: + - db_data:/var/lib/mysql -# Or manually: -cp .env.example .env -docker compose build -docker compose up -d -docker compose exec app php artisan key:generate -docker compose exec app php artisan migrate +volumes: + db_data: + app_storage: ``` -The application will be available at **http://localhost:8000** +### Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `APP_KEY` | Yes | Encryption key. Generate with: `echo "base64:$(openssl rand -base64 32)"` | +| `APP_URL` | Yes | Your domain (e.g., `https://meals.example.com`) | +| `DB_DATABASE` | Yes | Database name | +| `DB_USERNAME` | Yes | Database user | +| `DB_PASSWORD` | Yes | Database password | +| `DB_ROOT_PASSWORD` | Yes | MariaDB root password | +| `MAIL_HOST` | No | SMTP host for email notifications | +| `MAIL_PORT` | No | SMTP port (default: 587) | +| `MAIL_USERNAME` | No | SMTP username | +| `MAIL_PASSWORD` | No | SMTP password | +| `MAIL_FROM_ADDRESS` | No | From address for emails | ## 🔧 Development -### Starting the Development Environment +### NixOS / Nix ```bash -# Start all services -make dev - -# Or with Docker Compose directly -docker compose up -d +git clone https://forge.lvl0.xyz/lvl0/dishplanner.git +cd dishplanner +nix-shell ``` -**Available services:** -- **App**: http://localhost:8000 (Laravel + FrankenPHP) -- **Vite**: http://localhost:5173 (Asset hot-reload) -- **Mailhog**: http://localhost:8025 (Email testing) -- **Database**: localhost:3306 (MariaDB) +The shell will display available commands and optionally start the containers for you. -### Common Development Commands +#### Available Commands -```bash -# View logs -make logs # App logs -make logs-db # Database logs +| Command | Description | +|---------|-------------| +| `dev-up` | Start development environment | +| `dev-down` | Stop development environment | +| `dev-restart` | Restart containers | +| `dev-rebuild` | Full rebuild (removes volumes) | +| `dev-rebuild-quick` | Quick rebuild (keeps volumes) | +| `dev-logs [service]` | Follow logs | +| `dev-shell` | Enter app container | +| `dev-artisan ` | Run artisan commands | +| `dev-fix-permissions` | Fix Docker-created file permissions | -# Laravel commands -make artisan cmd="migrate" # Run artisan commands -make tinker # Start Laravel tinker -make test # Run tests +#### Services -# Database -make migrate # Run migrations -make seed # Seed database -make fresh # Fresh migrate with seeds +| Service | URL | +|---------|-----| +| App | http://localhost:8000 | +| Vite | http://localhost:5173 | +| Mailhog | http://localhost:8025 | +| MariaDB | localhost:3306 | -# Testing -make test # Run tests -composer test:coverage-html # Run tests with coverage report (generates coverage/index.html) +### Other Platforms -# Utilities -make shell # Enter app container -make db-shell # Enter database shell -make clear # Clear all caches -``` - -### Project Structure - -``` -dish-planner/ -├── app/ -│ ├── Livewire/ # Livewire components -│ │ ├── Auth/ # Authentication -│ │ ├── Dishes/ # Dish management -│ │ ├── Schedule/ # Schedule calendar -│ │ └── Users/ # User management -│ └── Models/ # Eloquent models -├── resources/ -│ └── views/ -│ └── livewire/ # Livewire component views -├── docker-compose.yml # Development environment -├── docker-compose.prod.yml # Production environment -├── Dockerfile # Production image -├── Dockerfile.dev # Development image -└── Makefile # Convenience commands -``` - -## 🚢 Production Deployment - -### Building for Production - -```bash -# Build production image -make prod-build - -# Start production environment -make prod - -# Or with Docker Compose -docker compose -f docker-compose.prod.yml build -docker compose -f docker-compose.prod.yml up -d -``` - -### Production Environment Variables - -Required environment variables for production: - -```env -# Required - Generate APP_KEY (see instructions below) -APP_KEY=base64:your-generated-key-here -APP_URL=https://your-domain.com - -# Database Configuration -DB_DATABASE=dishplanner -DB_USERNAME=dishplanner -DB_PASSWORD=strong-password-here -DB_ROOT_PASSWORD=strong-root-password - -# Optional Email Configuration -MAIL_HOST=your-smtp-host -MAIL_PORT=587 -MAIL_USERNAME=your-username -MAIL_PASSWORD=your-password -MAIL_FROM_ADDRESS=noreply@your-domain.com -``` - -#### Generating APP_KEY - -The APP_KEY is critical for encryption and must be kept consistent across deployments. Generate one using any of these methods: - -**Option 1: Using OpenSSL (Linux/Mac/Windows with Git Bash)** -```bash -echo "base64:$(openssl rand -base64 32)" -``` - -**Option 2: Using Node.js (Cross-platform)** -```bash -node -e "console.log('base64:' + require('crypto').randomBytes(32).toString('base64'))" -``` - -**Option 3: Using Python (Cross-platform)** -```bash -python -c "import base64, os; print('base64:' + base64.b64encode(os.urandom(32)).decode())" -``` - -**Option 4: Online Generator** -Generate a random 32-character string at https://randomkeygen.com/ and prepend with `base64:` - -⚠️ **Important**: Save this key securely! If lost, you won't be able to decrypt existing data. - -### Deployment with DockGE - -The production setup is optimized for DockGE deployment with just 2 containers: - -1. **app** - Laravel application with FrankenPHP -2. **db** - MariaDB database - -Simply import the `docker-compose.prod.yml` into DockGE and configure your environment variables. - -## 🛠️ Technology Stack - -- **Backend**: Laravel 12 with Livewire 3 -- **Web Server**: FrankenPHP (PHP 8.3 + Caddy) -- **Database**: MariaDB 11 -- **Frontend**: Blade + Livewire + Alpine.js -- **Styling**: TailwindCSS with custom dark theme -- **Assets**: Vite for bundling - -## 📦 Docker Architecture - -### Development (`docker-compose.yml`) -- **Hot reload** - Volume mounts for live code editing -- **Debug tools** - Xdebug configured for debugging -- **Email testing** - Mailhog for capturing emails -- **Asset watching** - Vite dev server for instant updates - -### Production (`docker-compose.prod.yml`) -- **Optimized** - Multi-stage builds with caching -- **Secure** - No debug tools, proper permissions -- **Health checks** - Automatic container monitoring -- **Single container** - FrankenPHP serves everything - -## 🔨 Make Commands Reference - -```bash -# Development -make dev # Start development environment -make dev-build # Build and start development -make dev-stop # Stop development environment -make dev-clean # Stop and remove volumes (CAUTION) - -# Production -make prod # Start production environment -make prod-build # Build production image -make prod-stop # Stop production environment -make prod-logs # Show production logs - -# Laravel -make artisan cmd="..." # Run artisan command -make composer cmd="..." # Run composer command -make npm cmd="..." # Run npm command -make migrate # Run migrations -make seed # Seed database -make fresh # Fresh migrate and seed -make tinker # Start tinker session -make test # Run tests - -# Utilities -make shell # Enter app container -make db-shell # Enter database shell -make logs # Show app logs -make logs-db # Show database logs -make clear # Clear all caches -make optimize # Optimize for production -``` - -## 🐛 Troubleshooting - -### Container won't start -```bash -# Check logs -docker compose logs app - -# Rebuild containers -docker compose build --no-cache -docker compose up -d -``` - -### Database connection issues -```bash -# Verify database is running -docker compose ps - -# Check database logs -docker compose logs db - -# Try manual connection -make db-shell -``` - -### Permission issues -```bash -# Fix storage permissions -docker compose exec app chmod -R 777 storage bootstrap/cache -``` - -### Clear all caches -```bash -make clear -# Or manually -docker compose exec app php artisan cache:clear -docker compose exec app php artisan config:clear -docker compose exec app php artisan route:clear -docker compose exec app php artisan view:clear -``` +Contributions welcome for development setup instructions on other platforms. ## 📄 License -This project is open-source software licensed under the [MIT license](LICENSE.md). - -## 🤝 Contributing - -Contributions are welcome! Please feel free to submit a Pull Request. - -1. Fork the repository -2. Create your feature branch (`git checkout -b feature/AmazingFeature`) -3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request +This project is open-source software licensed under the [AGPL-3.0 license](LICENSE.md). ## 📞 Support -For issues and questions, please use the [GitHub Issues](https://github.com/yourusername/dish-planner/issues) page. - ---- - -Built with ❤️ using Laravel and Livewire \ No newline at end of file +For issues and questions, please use [Forgejo Issues](https://forge.lvl0.xyz/lvl0/dishplanner/issues). \ No newline at end of file diff --git a/app/Enums/AppModeEnum.php b/app/Enums/AppModeEnum.php deleted file mode 100644 index 9a75881..0000000 --- a/app/Enums/AppModeEnum.php +++ /dev/null @@ -1,24 +0,0 @@ -user(); - - if ($planner->subscribed()) { - return redirect()->route('dashboard'); - } - - $plan = $request->input('plan', 'monthly'); - $priceId = $plan === 'yearly' - ? config('services.stripe.price_yearly') - : config('services.stripe.price_monthly'); - - return $planner->newSubscription('default', $priceId) - ->checkout([ - 'success_url' => route('subscription.success') . '?session_id={CHECKOUT_SESSION_ID}', - 'cancel_url' => route('subscription.index'), - ]); - } - - public function success(Request $request): RedirectResponse - { - $sessionId = $request->query('session_id'); - - if ($sessionId) { - $planner = $request->user(); - $session = Cashier::stripe()->checkout->sessions->retrieve($sessionId, [ - 'expand' => ['subscription'], - ]); - - if ($session->subscription && ! $planner->subscribed()) { - $subscription = $session->subscription; - - $planner->subscriptions()->create([ - 'type' => 'default', - 'stripe_id' => $subscription->id, - 'stripe_status' => $subscription->status, - 'stripe_price' => $subscription->items->data[0]->price->id ?? null, - 'quantity' => $subscription->items->data[0]->quantity ?? 1, - 'trial_ends_at' => $subscription->trial_end ? now()->setTimestamp($subscription->trial_end) : null, - 'ends_at' => null, - ]); - } - } - - return redirect()->route('dashboard')->with('success', 'Subscription activated!'); - } - - public function billing(Request $request) - { - $planner = $request->user(); - $subscription = $planner->subscription(); - - if (! $subscription) { - return redirect()->route('subscription.index'); - } - - $planType = match ($subscription->stripe_price) { - config('services.stripe.price_yearly') => 'Yearly', - config('services.stripe.price_monthly') => 'Monthly', - default => 'Unknown', - }; - - $nextBillingDate = null; - if ($subscription->stripe_status === 'active') { - try { - $stripeSubscription = Cashier::stripe()->subscriptions->retrieve($subscription->stripe_id); - $nextBillingDate = $stripeSubscription->current_period_end - ? now()->setTimestamp($stripeSubscription->current_period_end) - : null; - } catch (\Exception $e) { - // Stripe API error - continue without next billing date - } - } - - return view('billing.index', [ - 'subscription' => $subscription, - 'planner' => $planner, - 'planType' => $planType, - 'nextBillingDate' => $nextBillingDate, - ]); - } - - public function cancel(Request $request): RedirectResponse - { - $planner = $request->user(); - - if (! $planner->subscribed()) { - return back()->with('error', 'No active subscription found.'); - } - - $planner->subscription()->cancel(); - - return back()->with('success', 'Subscription canceled. Access will continue until the end of your billing period.'); - } - - public function billingPortal(Request $request) - { - return $request->user()->redirectToBillingPortal(route('billing')); - } -} diff --git a/app/Http/Middleware/RequireSaasMode.php b/app/Http/Middleware/RequireSaasMode.php deleted file mode 100644 index 0950eb6..0000000 --- a/app/Http/Middleware/RequireSaasMode.php +++ /dev/null @@ -1,19 +0,0 @@ -user(); - - if (! $planner?->subscribed()) { - return redirect()->route('subscription.index'); - } - - return $next($request); - } -} diff --git a/app/Models/Planner.php b/app/Models/Planner.php index f8307fd..c53777e 100644 --- a/app/Models/Planner.php +++ b/app/Models/Planner.php @@ -2,21 +2,23 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -use Laravel\Cashier\Billable; use Laravel\Sanctum\HasApiTokens; /** * @property int $id * @property static PlannerFactory factory($count = null, $state = []) + * @property Collection $users * @method static first() + * @method static create(array $array) */ class Planner extends Authenticatable { - use Billable, HasApiTokens, HasFactory, Notifiable; + use HasApiTokens, HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', @@ -34,4 +36,9 @@ public function schedules(): HasMany { return $this->hasMany(Schedule::class); } + + public function users(): HasMany + { + return $this->hasMany(User::class); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3067e8c..0bbbb1e 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,12 +4,10 @@ use App\Exceptions\CustomException; use App\Models\Dish; -use App\Models\Planner; use App\Models\Schedule; use App\Models\ScheduledUserDish; use App\Models\User; use App\Models\UserDish; -use Laravel\Cashier\Cashier; use DishPlanner\Dish\Policies\DishPolicy; use DishPlanner\Schedule\Policies\SchedulePolicy; use DishPlanner\ScheduledUserDish\Policies\ScheduledUserDishPolicy; @@ -47,8 +45,6 @@ public function render($request, Throwable $e) public function boot(): void { - Cashier::useCustomerModel(Planner::class); - Gate::policy(Dish::class, DishPolicy::class); Gate::policy(Schedule::class, SchedulePolicy::class); Gate::policy(ScheduledUserDish::class, ScheduledUserDishPolicy::class); diff --git a/app/helpers.php b/app/helpers.php deleted file mode 100644 index 59bc43a..0000000 --- a/app/helpers.php +++ /dev/null @@ -1,17 +0,0 @@ -isApp(); - } -} - -if (! function_exists('is_mode_saas')) { - function is_mode_saas(): bool - { - return AppModeEnum::current()->isSaas(); - } -} \ No newline at end of file diff --git a/bin/build-push.sh b/bin/build-push.sh deleted file mode 100755 index 532b43f..0000000 --- a/bin/build-push.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Build and push production image to Codeberg - -set -e - -# Configuration -REGISTRY="codeberg.org" -NAMESPACE="lvl0" -IMAGE_NAME="dish-planner" -TAG="${1:-latest}" - -echo "🔨 Building production image..." -podman build -f Dockerfile -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG} . - -echo "📤 Pushing to Codeberg registry..." -echo "Please ensure you're logged in to Codeberg:" -echo " podman login codeberg.org" - -podman push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG} - -echo "✅ Done! Image pushed to ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}" -echo "" -echo "To deploy in production:" -echo "1. Copy docker-compose.prod.yml to your server" -echo "2. Set required environment variables:" -echo " - APP_KEY (generate with: openssl rand -base64 32)" -echo " - APP_URL" -echo " - DB_DATABASE, DB_USERNAME, DB_PASSWORD, DB_ROOT_PASSWORD" -echo "3. Run: docker-compose -f docker-compose.prod.yml up -d" \ No newline at end of file diff --git a/bin/build_and_push.sh b/bin/build_and_push.sh index b3ce7d4..2c36dc5 100755 --- a/bin/build_and_push.sh +++ b/bin/build_and_push.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker build -t 192.168.178.152:50114/dishplanner-backend . -docker push 192.168.178.152:50114/dishplanner-backend +docker build -t forge.lvl0.xyz/lvl0/dishplanner:latest . +docker push forge.lvl0.xyz/lvl0/dishplanner:latest diff --git a/bin/start-dev b/bin/start-dev deleted file mode 100755 index 4d31519..0000000 --- a/bin/start-dev +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Get script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -# Detect Docker or Podman -CONTAINER_CLI="" -# Check if docker is actually podman in disguise -if command -v docker &> /dev/null && docker --version 2>&1 | grep -q "podman"; then - CONTAINER_CLI="podman" - echo -e "${GREEN}Using Podman (via docker alias)${NC}" -elif command -v docker &> /dev/null && docker info &> /dev/null; then - CONTAINER_CLI="docker" - echo -e "${GREEN}Using Docker${NC}" -elif command -v podman &> /dev/null; then - CONTAINER_CLI="podman" - echo -e "${GREEN}Using Podman${NC}" -else - echo -e "${RED}✗ Neither Docker nor Podman found. Please install one of them first.${NC}" - echo -e "${YELLOW}Install Docker: https://docs.docker.com/get-docker/${NC}" - echo -e "${YELLOW}Install Podman: https://podman.io/getting-started/installation${NC}" - exit 1 -fi - -echo -e "${GREEN}=== Dish Planner Development Setup ===${NC}\n" - -# Check if .env exists in backend -if [ ! -f "$PROJECT_ROOT/backend/.env" ]; then - echo -e "${YELLOW}Creating backend/.env from .env.example...${NC}" - cp "$PROJECT_ROOT/backend/.env.example" "$PROJECT_ROOT/backend/.env" - echo -e "${GREEN}✓ Created backend/.env${NC}\n" -fi - -# Ensure APP_PORT is set for Podman (can't use privileged port 80) -if [ "$CONTAINER_CLI" = "podman" ] && ! grep -q "^APP_PORT=" "$PROJECT_ROOT/backend/.env"; then - echo -e "${YELLOW}Setting APP_PORT=8000 for Podman (non-privileged port)...${NC}" - echo "APP_PORT=8000" >> "$PROJECT_ROOT/backend/.env" -fi - -# Backend setup -echo -e "${GREEN}=== Backend Setup ===${NC}" -cd "$PROJECT_ROOT/backend" - -# Install dependencies if vendor doesn't exist -if [ ! -d "vendor" ]; then - echo -e "${YELLOW}No vendor directory found. Installing dependencies with Docker...${NC}" - # Use a standalone PHP/Composer Docker image to install dependencies - # This is the recommended Laravel approach for first-time setup - # Configure for Docker or Podman - VOLUME_OPTS="$PROJECT_ROOT/backend:/var/www/html" - USER_OPTS="-u $(id -u):$(id -g)" - EXTRA_OPTS="" - - if [ "$CONTAINER_CLI" = "podman" ]; then - # Podman on SELinux systems needs :Z and --userns=keep-id to maintain user ID - VOLUME_OPTS="$VOLUME_OPTS:Z" - USER_OPTS="" - EXTRA_OPTS="--userns=keep-id" - fi - - $CONTAINER_CLI run --rm \ - $USER_OPTS \ - $EXTRA_OPTS \ - -v "$VOLUME_OPTS" \ - -w /var/www/html \ - docker.io/laravelsail/php84-composer:latest \ - composer install --ignore-platform-reqs - - echo -e "${GREEN}✓ Backend dependencies installed${NC}\n" -else - echo -e "${GREEN}✓ Backend dependencies already installed${NC}\n" -fi - -# Check if database volume exists - if not, we're doing fresh initialization -FRESH_DB=false -if ! $CONTAINER_CLI volume inspect sail-mysql &>/dev/null && ! $CONTAINER_CLI volume inspect backend_sail-mysql &>/dev/null; then - FRESH_DB=true - echo -e "${YELLOW}Fresh database initialization detected${NC}" -fi - -# Start containers using compose directly (docker-compose.override.yml is automatically used) -echo -e "${YELLOW}Starting backend containers...${NC}" -if $CONTAINER_CLI compose up -d 2>&1 | tee /tmp/compose-up.log; then - echo -e "${GREEN}✓ Containers started${NC}\n" -else - echo -e "${RED}✗ Failed to start containers. Check /tmp/compose-up.log for details${NC}" - exit 1 -fi - -# Wait for database to be ready -echo -e "${YELLOW}Waiting for database to be ready...${NC}" -MAX_ATTEMPTS=30 -ATTEMPT=0 -while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if $CONTAINER_CLI compose exec mysql mysqladmin ping -h localhost --silent 2>/dev/null; then - echo -e "${GREEN}✓ Database is ready${NC}" - break - fi - ATTEMPT=$((ATTEMPT + 1)) - echo -e "${YELLOW}Waiting for database... (attempt $ATTEMPT/$MAX_ATTEMPTS)${NC}" - sleep 2 -done - -if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo -e "${RED}✗ Database failed to become ready${NC}" - exit 1 -fi - -# Give MySQL extra time on fresh initialization to create users -if [ "$FRESH_DB" = true ]; then - echo -e "${YELLOW}Waiting for fresh database to complete initialization...${NC}" - sleep 10 -else - sleep 3 -fi - -# Run migrations -echo -e "${YELLOW}Running database migrations...${NC}" -$CONTAINER_CLI compose exec backend php artisan migrate --force - -# Check if database has data -echo -e "${YELLOW}Checking if database needs seeding...${NC}" -TABLE_COUNT=$($CONTAINER_CLI compose exec backend php artisan tinker --execute="echo \DB::table('users')->count();" 2>/dev/null | tail -1 | tr -d '[:space:]' || echo "0") -# Default to 0 if not a number -if ! [[ "$TABLE_COUNT" =~ ^[0-9]+$ ]]; then - TABLE_COUNT=0 -fi -if [ "$TABLE_COUNT" -eq "0" ]; then - echo -e "${YELLOW}Database is empty. Run seeders? (y/n)${NC}" - read -r response - if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then - $CONTAINER_CLI compose exec backend php artisan db:seed - echo -e "${GREEN}✓ Database seeded${NC}\n" - fi -fi - -echo -e "${GREEN}✓ Backend setup complete${NC}" -echo -e "${GREEN}Backend API running at: http://localhost:8000${NC}\n" - -# Display summary -echo -e "${GREEN}=== Development Environment Ready ===${NC}" -echo -e "${GREEN}Backend API:${NC} http://localhost:8000" -echo -e "${GREEN}Frontend:${NC} http://localhost:5173" -echo -e "${GREEN}Database:${NC} MySQL on localhost:3306" -echo -e "" -echo -e "${YELLOW}Note:${NC} Frontend container will install dependencies and start automatically." -echo -e "${YELLOW}To view frontend logs:${NC}" -echo -e " cd backend && $CONTAINER_CLI compose logs -f frontend" -echo -e "" -echo -e "${YELLOW}To stop all services:${NC}" -echo -e " cd backend && $CONTAINER_CLI compose down" diff --git a/bin/update.sh b/bin/update.sh index 7320639..2c864a3 100755 --- a/bin/update.sh +++ b/bin/update.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -echo "🔄 Pulling latest backend changes..." +echo "🔄 Pulling latest changes..." git pull origin main echo "📦 Installing PHP dependencies..." @@ -15,4 +15,4 @@ php artisan config:cache php artisan route:cache php artisan view:cache -echo "✅ Backend update complete!" +echo "✅ Update complete!" diff --git a/bootstrap/app.php b/bootstrap/app.php index 7fb61b3..5e69532 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,14 +1,11 @@ group(base_path('routes/web/subscription.php')); - }, ) ->withMiddleware(function (Middleware $middleware) { // Apply ForceJsonResponse only to API routes $middleware->api(ForceJsonResponse::class); - - $middleware->alias([ - 'subscription' => RequireSubscription::class, - 'saas' => RequireSaasMode::class, - ]); - - // Exclude Stripe webhook from CSRF verification - $middleware->validateCsrfTokens(except: [ - 'stripe/webhook', - ]); }) ->withExceptions(function (Exceptions $exceptions) { $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { diff --git a/build-push.sh b/build-push.sh deleted file mode 100755 index 532b43f..0000000 --- a/build-push.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Build and push production image to Codeberg - -set -e - -# Configuration -REGISTRY="codeberg.org" -NAMESPACE="lvl0" -IMAGE_NAME="dish-planner" -TAG="${1:-latest}" - -echo "🔨 Building production image..." -podman build -f Dockerfile -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG} . - -echo "📤 Pushing to Codeberg registry..." -echo "Please ensure you're logged in to Codeberg:" -echo " podman login codeberg.org" - -podman push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG} - -echo "✅ Done! Image pushed to ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}" -echo "" -echo "To deploy in production:" -echo "1. Copy docker-compose.prod.yml to your server" -echo "2. Set required environment variables:" -echo " - APP_KEY (generate with: openssl rand -base64 32)" -echo " - APP_URL" -echo " - DB_DATABASE, DB_USERNAME, DB_PASSWORD, DB_ROOT_PASSWORD" -echo "3. Run: docker-compose -f docker-compose.prod.yml up -d" \ No newline at end of file diff --git a/build_and_push.sh b/build_and_push.sh deleted file mode 100755 index b3ce7d4..0000000 --- a/build_and_push.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -docker build -t 192.168.178.152:50114/dishplanner-backend . -docker push 192.168.178.152:50114/dishplanner-backend diff --git a/composer.json b/composer.json index 2ecb17b..8f7b9ef 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,9 @@ "laravel", "framework" ], - "license": "MIT", + "license": "AGPL-3.0-only", "require": { "php": "^8.2", - "laravel/cashier": "^16.1", "laravel/framework": "^12.9.2", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", @@ -27,9 +26,6 @@ "phpunit/phpunit": "^11.0.1" }, "autoload": { - "files": [ - "app/helpers.php" - ], "psr-4": { "App\\": "app/", "DishPlanner\\": "src/DishPlanner/", diff --git a/config/app.php b/config/app.php index 09dd952..df3f5f0 100644 --- a/config/app.php +++ b/config/app.php @@ -28,18 +28,6 @@ 'env' => env('APP_ENV', 'production'), - /* - |-------------------------------------------------------------------------- - | Application Mode - |-------------------------------------------------------------------------- - | - | Determines the application deployment mode: 'app' for self-hosted, - | 'saas' for multi-tenant SaaS, 'demo' for demonstration instances. - | - */ - - 'mode' => env('APP_MODE', 'app'), - /* |-------------------------------------------------------------------------- | Application Debug Mode diff --git a/config/services.php b/config/services.php index cf3ce61..27a3617 100644 --- a/config/services.php +++ b/config/services.php @@ -35,14 +35,4 @@ ], ], - 'stripe' => [ - 'key' => env('STRIPE_KEY'), - 'secret' => env('STRIPE_SECRET'), - 'webhook' => [ - 'secret' => env('STRIPE_WEBHOOK_SECRET'), - ], - 'price_monthly' => env('STRIPE_PRICE_MONTHLY'), - 'price_yearly' => env('STRIPE_PRICE_YEARLY'), - ], - ]; diff --git a/database/migrations/2026_01_06_000525_create_customer_columns.php b/database/migrations/2026_01_06_000525_create_customer_columns.php deleted file mode 100644 index 131d232..0000000 --- a/database/migrations/2026_01_06_000525_create_customer_columns.php +++ /dev/null @@ -1,34 +0,0 @@ -string('stripe_id')->nullable()->index(); - $table->string('pm_type')->nullable(); - $table->string('pm_last_four', 4)->nullable(); - $table->timestamp('trial_ends_at')->nullable(); - }); - } - - public function down(): void - { - Schema::table('planners', function (Blueprint $table) { - $table->dropIndex([ - 'stripe_id', - ]); - - $table->dropColumn([ - 'stripe_id', - 'pm_type', - 'pm_last_four', - 'trial_ends_at', - ]); - }); - } -}; diff --git a/database/migrations/2026_01_06_000526_create_subscriptions_table.php b/database/migrations/2026_01_06_000526_create_subscriptions_table.php deleted file mode 100644 index 9043296..0000000 --- a/database/migrations/2026_01_06_000526_create_subscriptions_table.php +++ /dev/null @@ -1,37 +0,0 @@ -id(); - $table->foreignId('planner_id'); - $table->string('type'); - $table->string('stripe_id')->unique(); - $table->string('stripe_status'); - $table->string('stripe_price')->nullable(); - $table->integer('quantity')->nullable(); - $table->timestamp('trial_ends_at')->nullable(); - $table->timestamp('ends_at')->nullable(); - $table->timestamps(); - - $table->index(['planner_id', 'stripe_status']); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('subscriptions'); - } -}; diff --git a/database/migrations/2026_01_06_000527_create_subscription_items_table.php b/database/migrations/2026_01_06_000527_create_subscription_items_table.php deleted file mode 100644 index 420e23f..0000000 --- a/database/migrations/2026_01_06_000527_create_subscription_items_table.php +++ /dev/null @@ -1,34 +0,0 @@ -id(); - $table->foreignId('subscription_id'); - $table->string('stripe_id')->unique(); - $table->string('stripe_product'); - $table->string('stripe_price'); - $table->integer('quantity')->nullable(); - $table->timestamps(); - - $table->index(['subscription_id', 'stripe_price']); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('subscription_items'); - } -}; diff --git a/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php b/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php deleted file mode 100644 index 033bb82..0000000 --- a/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php +++ /dev/null @@ -1,28 +0,0 @@ -string('meter_id')->nullable()->after('stripe_price'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('subscription_items', function (Blueprint $table) { - $table->dropColumn('meter_id'); - }); - } -}; diff --git a/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php b/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php deleted file mode 100644 index b157b3a..0000000 --- a/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php +++ /dev/null @@ -1,28 +0,0 @@ -string('meter_event_name')->nullable()->after('quantity'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('subscription_items', function (Blueprint $table) { - $table->dropColumn('meter_event_name'); - }); - } -}; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f39bab7..088e6c7 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,7 +1,7 @@ # Production Docker Compose services: app: - image: codeberg.org/lvl0/dish-planner:latest + image: forge.lvl0.xyz/lvl0/dishplanner:latest container_name: dishplanner_app restart: always ports: diff --git a/frontend-old/.dockerignore b/frontend-old/.dockerignore deleted file mode 100644 index 9b8d514..0000000 --- a/frontend-old/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -.react-router -build -node_modules -README.md \ No newline at end of file diff --git a/frontend-old/.gitignore b/frontend-old/.gitignore deleted file mode 100644 index 9b7c041..0000000 --- a/frontend-old/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.DS_Store -/node_modules/ - -# React Router -/.react-router/ -/build/ diff --git a/frontend-old/Dockerfile b/frontend-old/Dockerfile deleted file mode 100644 index 207bf93..0000000 --- a/frontend-old/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:20-alpine AS development-dependencies-env -COPY . /app -WORKDIR /app -RUN npm ci - -FROM node:20-alpine AS production-dependencies-env -COPY ./package.json package-lock.json /app/ -WORKDIR /app -RUN npm ci --omit=dev - -FROM node:20-alpine AS build-env -COPY . /app/ -COPY --from=development-dependencies-env /app/node_modules /app/node_modules -WORKDIR /app -RUN npm run build - -FROM node:20-alpine -COPY ./package.json package-lock.json /app/ -COPY --from=production-dependencies-env /app/node_modules /app/node_modules -COPY --from=build-env /app/build /app/build -WORKDIR /app -CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/frontend-old/README.md b/frontend-old/README.md deleted file mode 100644 index 5c4780a..0000000 --- a/frontend-old/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Welcome to React Router! - -A modern, production-ready template for building full-stack React applications using React Router. - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default) - -## Features - -- 🚀 Server-side rendering -- ⚡️ Hot Module Replacement (HMR) -- 📦 Asset bundling and optimization -- 🔄 Data loading and mutations -- 🔒 TypeScript by default -- 🎉 TailwindCSS for styling -- 📖 [React Router docs](https://reactrouter.com/) - -## Getting Started - -### Installation - -Install the dependencies: - -```bash -npm install -``` - -### Development - -Start the development server with HMR: - -```bash -npm run dev -``` - -Your application will be available at `http://localhost:5173`. - -## Building for Production - -Create a production build: - -```bash -npm run build -``` - -## Deployment - -### Docker Deployment - -To build and run using Docker: - -```bash -docker build -t my-app . - -# Run the container -docker run -p 3000:3000 my-app -``` - -The containerized application can be deployed to any platform that supports Docker, including: - -- AWS ECS -- Google Cloud Run -- Azure Container Apps -- Digital Ocean App Platform -- Fly.io -- Railway - -### DIY Deployment - -If you're familiar with deploying Node applications, the built-in app server is production-ready. - -Make sure to deploy the output of `npm run build` - -``` -├── package.json -├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) -├── build/ -│ ├── client/ # Static assets -│ └── server/ # Server-side code -``` - -## Styling - -This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. - ---- - -Built with ❤️ using React Router. diff --git a/frontend-old/app/app.css b/frontend-old/app/app.css deleted file mode 100644 index 2187b39..0000000 --- a/frontend-old/app/app.css +++ /dev/null @@ -1,18 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&family=Syncopate:wght@400;700&display=swap'); -@import "tailwindcss"; - -@import "./styles/main.css"; - -@theme { - --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - -html, -body { - @apply bg-white dark:bg-gray-950; - - @media (prefers-color-scheme: dark) { - color-scheme: dark; - } -} diff --git a/frontend-old/app/components/Spinner.tsx b/frontend-old/app/components/Spinner.tsx deleted file mode 100644 index f170616..0000000 --- a/frontend-old/app/components/Spinner.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const Spinner = () => { - - return ( -
- - - - -
- ) - -} - -export default Spinner \ No newline at end of file diff --git a/frontend-old/app/components/features/OnboardingBanner.tsx b/frontend-old/app/components/features/OnboardingBanner.tsx deleted file mode 100644 index ee97858..0000000 --- a/frontend-old/app/components/features/OnboardingBanner.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Link } from "react-router" -import useRoutes from "@/hooks/useRoutes" -import { UserType } from "@/types/UserType" -import { DishType } from "@/types/DishType" - -interface Props { - dishes: DishType[], - users: UserType[] -} - -const OnboardingBanner = ({ dishes, users }: Props) => { - const routes = useRoutes(); - - const steps = [ - { - label: "Create a user", - href: routes.user.create(), - count: users.length - }, { - label: "Create a dish", - href: routes.dish.create(), - count: dishes.length - } - ] - - return ( -
-
Welcome to DishPlanner
-
To get you started, please follow these steps to set up your account. This will ensure a better - experience. -
- - { - steps.map((step, index) => ( -
- { - step.count === 0 - ? { step.label } - :
{ step.label }
- } -
- )) - } -
- ) -} - -export default OnboardingBanner; \ No newline at end of file diff --git a/frontend-old/app/components/features/auth/LoginForm.tsx b/frontend-old/app/components/features/auth/LoginForm.tsx deleted file mode 100644 index 3e693d8..0000000 --- a/frontend-old/app/components/features/auth/LoginForm.tsx +++ /dev/null @@ -1,98 +0,0 @@ - -import React, { useEffect, useState } from 'react'; -import { Link, useLocation, useNavigate } from 'react-router'; -import { useAuth } from '@/context/AuthContext'; -import { login } from "@/utils/api/auth"; -import useRoutes from "@/hooks/useRoutes"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import Alert from "@/components/ui/Alert"; - -const LoginForm = () => { - const { login: authLogin } = useAuth(); - const routes = useRoutes(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [alertSuccess, setAlertSuccess] = useState([]) - const navigate = useNavigate(); - const location = useLocation(); - const searchParams = new URLSearchParams(location.search); - const isRegistered = searchParams.get('registered') === 'true'; - - useEffect(() => { - if (isRegistered) { - setAlertSuccess(['Registration successful!',' You can now log in.']); - - const timer = setTimeout(() => { - const params = new URLSearchParams(searchParams.toString()); - params.delete('registered'); - const newUrl = `${window.location.pathname}?${params.toString()}`; - - navigate(newUrl, { replace: true }); - }, 3000); - - return () => clearTimeout(timer) - } - }, [isRegistered, navigate, searchParams]) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - try { - await login(email, password); - authLogin(); - navigate('/', { replace: true }); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Login failed'; - setError(errorMessage); - } - }; - - return ( -
-
- DISH PLANNER -
-
- { alertSuccess.length > 0 && - - {alertSuccess.map((msg, index) => ( - - {msg} -
-
- ))} -
- } -
- {error &&

{error}

} - setEmail(e.target.value)} - required - className="w-full p-2 mb-4 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value)} - required - className="w-full p-2 mb-4 border rounded text-secondary border-secondary bg-gray-600" - /> - Login - - Create an account - -
-
-
- ) -} - -export default LoginForm \ No newline at end of file diff --git a/frontend-old/app/components/features/auth/RegisterForm.tsx b/frontend-old/app/components/features/auth/RegisterForm.tsx deleted file mode 100644 index 908d624..0000000 --- a/frontend-old/app/components/features/auth/RegisterForm.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { useState } from 'react'; -import { register } from "@/utils/api/auth"; -import useRoutes from "@/hooks/useRoutes"; -import SectionTitle from "@/components/ui/SectionTitle"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import { Link, useNavigate } from "react-router" - -const RegisterForm = () => { - const routes = useRoutes(); - const [name, setName] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [passwordAgain, setPasswordAgain] = useState(''); - const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [isRegistered, setIsRegistered] = useState(false); - const navigate = useNavigate(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (password !== passwordAgain) { - setError("Passwords do not match."); - return; - } - - try { - setIsLoading(true); - - await register(name, email, password, passwordAgain); - // navigate('/login?registered=true', { replace: true }); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Registration\n failed'; - setError(errorMessage); - } finally { - setIsRegistered(true); - setIsLoading(false); - } - }; - - if (isRegistered) { - return
- Registration successful! - Please continue to the login page. -
- } - - return ( -
-
- DISH PLANNER -
-
-
-

Register

- { error &&

{ error }

} - setName(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setEmail(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPasswordAgain(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - - Create Account - - - Back to Login - -
-
-
- ) -} - -export default RegisterForm \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx b/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx deleted file mode 100644 index 974cd11..0000000 --- a/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import {addUserToDish} from "@/utils/api/dishApi"; -import OutlineButton from "@/components/ui/Buttons/OutlineButton"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const AddUserToDishForm = ({ dish, reloadDish }: Props) => { - const [showAdd, setShowAdd] = useState(false); - const [selectedUser, setSelectedUser] = useState("-1"); - const { users, isLoading: isUsersLoading } = useFetchUsers(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (selectedUser === "-1") { - alert("Please select a valid user."); - return; - } - - const userToAdd = users.find((user: UserType) => user.id === parseInt(selectedUser)); - - if (!userToAdd) { - alert("User not found."); - return; - } - - addUserToDish(dish.id, userToAdd.id) - .then(() => { - setShowAdd(false); - setSelectedUser("-1"); - reloadDish(); - }) - .catch(() => { - alert("Failed to add user, please try again."); - }); - }; - - if (isUsersLoading) { - return ; - } - - const remainingUsers = users.filter( - (user: UserType) => - !dish.users.find((dishUser: UserType) => dishUser.id === user.id) - ); - - return ( - <> - setShowAdd(!showAdd)} - disabled={remainingUsers.length === 0} - type="button" - > - Add User - - - { showAdd && ( -
-
-
-
- -
- -
- - Add User - -
-
-
-
- )} - - ); -}; - -export default AddUserToDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/CreateDishForm.tsx b/frontend-old/app/components/features/dishes/CreateDishForm.tsx deleted file mode 100644 index 69a139f..0000000 --- a/frontend-old/app/components/features/dishes/CreateDishForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState } from "react"; -import { useNavigate } from "react-router"; -import { createDish } from "~/utils/api/dishApi"; -import PageTitle from "~/components/ui/PageTitle"; -import Alert from "~/components/ui/Alert"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "~/components/ui/Hr" - -const CreateDishForm = () => { - const navigate = useNavigate() - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - // Validate client-side input - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await createDish(name); - if (result) { - navigate('/dishes') - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred."); - } finally { - setLoading(false); - } - } - - return ( -
-
- Create Dish -
- -
- { error && ( - { error } - ) } - -
- - setName(e.target.value) } // Update the name state on change - className="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-secondary focus:bg-gray-900" - placeholder="Enter dish name" - /> -
- - - { loading ? "Saving..." : "Save Changes" } - -
- -
- - } - > - Back to dishes - -
- ); - - -}; - -export default CreateDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/Dish.tsx b/frontend-old/app/components/features/dishes/Dish.tsx deleted file mode 100644 index 44128c7..0000000 --- a/frontend-old/app/components/features/dishes/Dish.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {DishType} from "~/types/DishType"; - -import {PencilIcon, TrashIcon} from '@heroicons/react/24/solid' -import { Link } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import {UserType} from "~/types/UserType"; -import Card from "~/components/layout/Card"; - -const Dish = ({ dish }: { dish: DishType}) => { - const routes = useRoutes(); - - return ( - -
-

{ dish.name }

- - { - dish.users.map((user: UserType) => ( -
{user.name.slice(0, 1)}
- )) - } -
-
- -
- -
- - -
- -
- -
-
- ) -} - -export default Dish \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/DishCard.tsx b/frontend-old/app/components/features/dishes/DishCard.tsx deleted file mode 100644 index d36a656..0000000 --- a/frontend-old/app/components/features/dishes/DishCard.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {DishType} from "@/types/DishType"; - -interface Props { - user: UserType, - dish: DishType, -} - -const DishCard = ({ user, dish }: Props) => { - return ( -
-
- { user.name.slice(0, 1) } -
-
- { dish ? dish.name : '-' } -
-
- ) -} - -export default DishCard \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/EditDishForm.tsx b/frontend-old/app/components/features/dishes/EditDishForm.tsx deleted file mode 100644 index a35002e..0000000 --- a/frontend-old/app/components/features/dishes/EditDishForm.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, {useState} from "react"; -import { useNavigate } from "react-router"; -import Alert from "~/components/ui/Alert"; -import {updateDish} from "~/utils/api/dishApi"; -import {DishType} from "~/types/DishType"; -import useRoutes from "~/hooks/useRoutes"; -import Spinner from "~/components/Spinner"; -import Button from "~/components/ui/Button" - -interface Props { - dish: DishType -} - -const EditDishForm = ({ dish }: Props) => { - const [name, setName] = useState(dish.name); - const [error, setError] = useState(""); - const navigate = useNavigate() - const [loading, setLoading] = useState(false); - const routes = useRoutes(); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await updateDish(dish.id, name); - if (result) { - navigate(routes.dish.index()) - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred"); - } finally { - setLoading(false); // Reset loading state - } - } - - if (loading) { - return ; - } - - return ( -
- { - error != '' && { error } - } - - {/* Dish name input */} -
- - setName(e.target.value)} // Update the name state on change - className="p-2 border rounded w-full bg-gray-500 border-secondary background-secondary" - /> -
- - {/* Save button */} - -
- ); -} - -export default EditDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx b/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx deleted file mode 100644 index 1ac08cd..0000000 --- a/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from "react"; -import SectionTitle from "@/components/ui/SectionTitle"; -import {syncUserDishRecurrences} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import {UserDishType} from "@/types/ScheduledUserDishType"; -import {RecurrenceType} from "@/types/RecurrenceType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - userDish: UserDishType - onSubmit: () => void -} - -const EditDishUserCardEditForm = ({ userDish, onSubmit}: Props) => { - const weeklyRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\WeeklyRecurrence') - const minimumRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\MinimumRecurrence') - - const wv = weeklyRecurrence ? weeklyRecurrence.value : undefined - const mv = minimumRecurrence ? minimumRecurrence.value : undefined - - const [isWeeklyOn, setIsWeeklyOn] = React.useState(weeklyRecurrence !== undefined); - const [isMinimumOn, setIsMinimumOn] = React.useState(minimumRecurrence !== undefined); - const [weekday, setWeekday] = React.useState(wv ?? 0); - const [minimumValue, setMinimumValue] = React.useState(mv ?? 7); - const [loading, setLoading] = React.useState(false); - - const handleSubmit = () => { - const recurrences = [] - - if (isWeeklyOn) { - recurrences.push({ - type: 'App\\Models\\WeeklyRecurrence', - value: weekday, - }); - } - - if (isMinimumOn) { - recurrences.push({ - type: 'App\\Models\\MinimumRecurrence', - value: minimumValue, - }); - } - - setLoading(true) - syncUserDishRecurrences(userDish.dish.id, userDish.user.id, recurrences as RecurrenceType[]) - .then((data) => console.log('request data', data)) - .finally(() => { - setLoading(false) - onSubmit() - }) - } - - if (loading) { - return ; - } - - return ( -
- Recurrences - -
-
- setIsWeeklyOn(!isWeeklyOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- { - isWeeklyOn && ( -
- - -
- ) - } -
- -
-
- setIsMinimumOn(!isMinimumOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-500 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- - { - isMinimumOn && ( -
- setMinimumValue(parseInt(e.currentTarget.value))} min="0" max="365" className="background-secondary border-secondary border-2 w-12 px-2" /> - -
- ) - } -
- - Save -
- ); -} - -export default EditDishUserCardEditForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx b/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx deleted file mode 100644 index deff7c3..0000000 --- a/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import {RecurrenceType} from "@/types/RecurrenceType"; - -interface Props { - recurrences: RecurrenceType[]; -} - -const RecurrenceLabels = ({recurrences}: Props) => { - const weeklyRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\WeeklyRecurrence'); - const minimumRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\MinimumRecurrence'); - - const renderWeeklyRecurrence = () => { - if (weeklyRecurrences == undefined || weeklyRecurrences.length == 0) { - return ''; - } - - const weekdayString = (() => { - switch (weeklyRecurrences[0].value) { - case 0: return "Sunday" - case 1: return "Monday" - case 2: return "Tuesday"; - case 3: return "Wednesday"; - case 4: return "Thursday"; - case 5: return "Friday"; - case 6: return "Saturday"; - default: return "Invalid day"; - } - }) - - return ( -
- { weekdayString() } -
- ) - } - const renderMinimumRecurrence = () => { - if (minimumRecurrences == undefined || minimumRecurrences.length == 0) { - return ''; - } - - return ( -
- min: { minimumRecurrences[0].value } -
- ) - } - - return <> - { renderWeeklyRecurrence() } - { renderMinimumRecurrence() } - ; -}; - -export default RecurrenceLabels; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/SyncUsersForm.tsx b/frontend-old/app/components/features/dishes/SyncUsersForm.tsx deleted file mode 100644 index d1f7264..0000000 --- a/frontend-old/app/components/features/dishes/SyncUsersForm.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import UserDishCard from "@/components/features/dishes/UserDishCard"; -import SectionTitle from "@/components/ui/SectionTitle"; -import AddUserToDishForm from "@/components/features/dishes/AddUserToDishForm"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const SyncUsersForm = ({ dish, reloadDish }: Props) => { - return ( -
- Users - - - - {dish.users.map((user: UserType) => ( - - ))} -
- ); -}; - -export default SyncUsersForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/UserDishCard.tsx b/frontend-old/app/components/features/dishes/UserDishCard.tsx deleted file mode 100644 index 5681434..0000000 --- a/frontend-old/app/components/features/dishes/UserDishCard.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, {useEffect} from "react"; -import {DishType} from "~/types/DishType"; -import {UserType} from "~/types/UserType"; -import { Link } from "react-router"; -import {PencilIcon, TrashIcon} from "@heroicons/react/24/solid"; -import {removeUserFromDish} from "~/utils/api/dishApi"; -import EditDishUserCardEditForm from "~/components/features/dishes/EditDishUserCardEditForm"; -import {getUserDishForUserAndDish} from "~/utils/api/usersApi"; -import Spinner from "~/components/Spinner"; -import RecurrenceLabels from "~/components/features/dishes/RecurrenceLabels"; -import {UserDishType} from "~/types/ScheduledUserDishType"; - -interface Props { - dish: DishType - user: UserType - reloadDish: () => void -} - -const UserDishCard = ({dish, user, reloadDish}: Props) => { - const [userDish, setUserDish] = React.useState(null); - const [userDishLoading, setUserDishLoading] = React.useState(true); - const [isEditMode, setIsEditMode] = React.useState(false); - - useEffect(() => { - getUserDishForUserAndDish(user.id, dish.id) - .then((userDish) => setUserDish(userDish)) - .finally(() => setUserDishLoading(false)) - }, [dish, user]); - - const handleRemove = () => { - removeUserFromDish(dish.id, user.id) - .then(() => reloadDish()) - .catch(() => { - alert("Failed to remove user, please try again."); - }); - }; - - if (userDishLoading || !userDish) { - return - } - - const onUserCardSubmit = () => { - setIsEditMode(false); - reloadDish() - } - - return ( -
-
-
- {user.name} -
- -
- -
- -
- setIsEditMode(!isEditMode)} to="#"> -
- -
- -
-
- -
- -
- -
-
- - {isEditMode && ( -
- -
- )} -
- ); -} - -export default UserDishCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx b/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx deleted file mode 100644 index 46ccdf0..0000000 --- a/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Link } from "react-router"; -import React from "react"; -import useRoutes from "~/hooks/useRoutes"; -import classNames from "classnames"; - -interface Props { - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; - handleLogout: (e: React.MouseEvent) => void; -} - -const divStyles = classNames( - 'absolute', 'text-xxl', 'rounded-b', 'top-full mt-1', 'left-0', 'right-0', '', 'py-2', - 'bg-gray-600', 'border-secondary', 'shadow-md', 'flex', 'flex-col', 'space-y-3', - 'md:hidden' -) - -const linkStyles = classNames( - 'border-b-2', 'border-secondary', 'uppercase', - 'text-primary', 'hover:background-secondary', 'pb-2', 'pl-5', - 'space-grotesk', 'text-xl' -) - -const MobileDropdownMenu = ({ isOpen, setIsOpen, handleLogout }: Props) => { - const routes = useRoutes(); - - if (!isOpen) return null; - - return ( -
- setIsOpen(false)} - > - Home - - setIsOpen(false)} - > - Dishes - - setIsOpen(false)} - > - Users - - setIsOpen(false)} - > - History - - - Logout - -
- ) -} - -export default MobileDropdownMenu \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/HistoricalDishes.tsx b/frontend-old/app/components/features/schedule/HistoricalDishes.tsx deleted file mode 100644 index ad3718f..0000000 --- a/frontend-old/app/components/features/schedule/HistoricalDishes.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client" - -import {useEffect, useState} from "react"; -import {DateTime} from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import {ScheduleType} from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import {listSchedule} from "@/utils/api/scheduleApi"; - -const HistoricalDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const yesterday = DateTime.now().minus({ days: 1 }).toFormat('yyyy-LL-dd'); - - useEffect(() => { - listSchedule(undefined, yesterday) - .then((dishes: ScheduleType[]) => dishes - .sort((a: ScheduleType, b: ScheduleType) => new Date(b.date).getTime() - new Date(a.date).getTime()) - ) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)) - }, [yesterday]); - - if (isLoading) { - return ; - } - - if (!schedule || Object.keys(schedule).length === 0) { - return ( -
- No dishes scheduled -
- ); - } - - return
- History - -
-} - -export default HistoricalDishes \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx b/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx deleted file mode 100644 index fce02c2..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import ScheduleDayCard from "@/components/features/schedule/dayCard/ScheduleDayCard"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; - -const generateDates = (startDate: string, days: number): string[] => { - const dates = []; - const start = new Date(startDate); - - for (let i = 0; i < days; i++) { - const currentDate = new Date(start); - currentDate.setDate(start.getDate() + i); - dates.push(currentDate.toISOString().split('T')[0]); - } - - return dates; -}; - - -const fillCalendar = (schedules: ScheduleType[]): FilledScheduleType[] => { - /* -Array(14) - 0: - date: "2025-05-05" - id: 2 - is_skipped: false - scheduled_user_dishes: [] - */ - - const dates = generateDates((new Date()).toISOString().split('T')[0], 31) - - return dates.map((date): FilledScheduleType => { - console.log(date) - - const schedule = schedules.find((schedule: ScheduleType) => schedule.date == date) - - if (schedule) { - return schedule - } - - return { - date, - scheduled_user_dishes: [] - } - }) -} - -interface Props { - schedule: ScheduleType[]; -} - -const ScheduleCalendar = ({ schedule }: Props) => { - const {users, isLoading: areUsersLoading} = useFetchUsers(); - - if (areUsersLoading) return - - const fullCalendar = fillCalendar(schedule) - - return ( -
- { fullCalendar.map((schedule, index) => ( - - ))} -
- ) -} - -export default ScheduleCalendar \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx b/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx deleted file mode 100644 index df5db7b..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { ScheduleType } from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import PageTitle from "@/components/ui/PageTitle"; -import { getScheduleForDate, scheduleUserDish, updateScheduleForDate } from "@/utils/api/scheduleApi"; -import { UserDishType } from "@/types/ScheduledUserDishType"; -import Label from "@/components/ui/Label"; -import SectionTitle from "@/components/ui/SectionTitle"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import { listUserDishes } from "@/utils/api/userDishApi"; -import scheduleBuilder from "@/utils/scheduleBuilder"; -import transformDate from "@/utils/dateBuilder"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" -import Button from "@/components/ui/Button" - -interface Props { - date: string; -} - -const ScheduleEditForm = ({ date }: Props) => { - const [schedule, setSchedule] = useState() - const [userDishes, setUserDishes] = useState([]) - const [isScheduleLoading, setIsScheduleLoading] = useState(true); - const [areUserDishesLoading, setAreUserDishesLoading] = useState(true); - const { users } = useFetchUsers(); - - useEffect(() => { - getScheduleForDate(date) - .then((sched: ScheduleType) => setSchedule(sched)) - .finally(() => setIsScheduleLoading(false)) - }, [date]); - - - useEffect(() => { - listUserDishes() - .then((user_dishes: UserDishType[]) => setUserDishes(user_dishes)) - .finally(() => setAreUserDishesLoading(false)) - }, []); - - const handleSkipDay = () => { - updateScheduleForDate(date, true) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleUnskipDay = () => { - updateScheduleForDate(date, false) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleChange = (e: React.ChangeEvent, userId: number) => { - const userDishId = parseInt(e.currentTarget.value); - - if (userDishId === 0) { - scheduleUserDish(date, userId, null, true).then(() => window.location.reload()); - return; - } - - scheduleUserDish(date, userId, userDishId).then(() => window.location.reload()); - } - - if (isScheduleLoading || areUserDishesLoading || !schedule) { - return - } - - const scheduleData = scheduleBuilder(schedule, users, userDishes) - - return
-
-
- Edit Day -
-
- { transformDate(schedule.date) } -
-
- -
- - { - userDishes.length === 0 &&
-
No dishes found assigned to this user.
-
Go ahead and add some first, or choose to skip the day.
-
(dishes ={`>`} edit ={`>`} add user)
-
- } - - { schedule.is_skipped - ? - : ( - <> - { - scheduleData - .map((scheduleData) =>
-
{ scheduleData.user.name }
-
- -
-
) - } - - ) - } - -
Changes are saved automatically
- -
- -
- -
{ - schedule.is_skipped - ? - : - }
-
-
-} - -export default ScheduleEditForm \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx b/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx deleted file mode 100644 index 3a27044..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState} from "react"; -import Modal from "@/components/ui/Modal"; -import ScheduleRegenerateForm from "@/components/features/schedule/ScheduleRegenerateForm"; -import {ArrowPathIcon} from "@heroicons/react/16/solid"; - -interface ScheduleRegenerateButtonProps { - onModalClose?: () => void; -} - -const ScheduleRegenerateButton = ({ onModalClose }: ScheduleRegenerateButtonProps) => { - const [open, setOpen] = useState(false); - - const handleCloseModal = () => { - setOpen(false) - if (onModalClose) { - onModalClose() - } - } - - const modalChildren = handleCloseModal()}/> - const buttonChild =
-
- - return -}; - -export default ScheduleRegenerateButton; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx b/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx deleted file mode 100644 index 3a9a4bf..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {DialogTitle} from "@headlessui/react"; -import Toggle from "@/components/ui/Toggle"; -import {useEffect, useState} from "react"; -import {generateSchedule} from "@/utils/api/scheduleApi"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface ScheduleRegenerateFormProps { - closeModal: () => void; -} - -const ScheduleRegenerateForm = ({closeModal}: ScheduleRegenerateFormProps) => { - const [overwrite, setOverwrite] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - }, [overwrite]); - - const close = () => { - closeModal(); - } - - const handleToggle = () => { - setOverwrite(!overwrite) - } - - const handleSubmit = () => { - generateSchedule(overwrite) - .then(() => close()) - .catch((err) => setError(err)) - } - - return <> -
-
-
- - Regenerate Schedule - -
-
- { - error && { error } - } -
- -
-
- -
-
-
- - -
-
-
-
- handleSubmit()} - className="inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold shadow-xs sm:ml-3 sm:w-auto" - > - Regenerate - - close()} - className="mt-3 inline-flex w-full justify-center rounded-md bg-gray-500 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs border-secondary ring-inset sm:mt-0 sm:w-auto" - > - Cancel - -
- ; -}; - -export default ScheduleRegenerateForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/UpcomingDishes.tsx b/frontend-old/app/components/features/schedule/UpcomingDishes.tsx deleted file mode 100644 index 99f69d4..0000000 --- a/frontend-old/app/components/features/schedule/UpcomingDishes.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import { DateTime } from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import Spinner from "@/components/Spinner"; -import { ScheduleType } from "@/types/ScheduleType"; -import { listSchedule } from "@/utils/api/scheduleApi"; -import OnboardingBanner from "@/components/features/OnboardingBanner" -import { useFetchUsers } from "@/hooks/useFetchUsers" -import { useFetchDishes } from "@/hooks/useFetchDishes" -import ScheduleRegenerateButton from "@/components/features/schedule/ScheduleRegenerateButton"; - -const UpcomingDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const today = DateTime.now().toFormat("yyyy-LL-dd"); - - const fetchSchedule = useCallback(() => { - setIsLoading(true); - listSchedule(today) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)); - }, [today]); - - useEffect(() => { - fetchSchedule(); - }, [fetchSchedule]); - - const { users, isLoading: areUsersLoading } = useFetchUsers(); - const { dishes, isLoading: areDishesLoading } = useFetchDishes(); - - if (isLoading || areUsersLoading || areDishesLoading) { - return ; - } - - if (users.length === 0 || dishes.length === 0) { - return - } - - return ( -
-
-
- Schedule -
-
- -
-
- { - !schedule || Object.keys(schedule).length === 0 - ?
No dishes scheduled
- : - } -
- ); -}; - -export default UpcomingDishes; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/UserDishEditCard.tsx b/frontend-old/app/components/features/schedule/UserDishEditCard.tsx deleted file mode 100644 index 653efbd..0000000 --- a/frontend-old/app/components/features/schedule/UserDishEditCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {FormEvent, useMemo, useState} from "react"; -import {DishType} from "@/types/DishType"; -import {ScheduledUserDishType} from "@/types/ScheduledUserDishType"; -import {updateScheduledUserDish} from "@/utils/api/scheduledUserDishesApi"; -import Alert from "@/components/ui/Alert"; -import classNames from "classnames"; - -interface Props { - scheduledUserDish: ScheduledUserDishType - allDishes: DishType[] -} - -const UserDishEditCard = ({ scheduledUserDish, allDishes }: Props) => { - const [selectedUserDishId, setSelectedUserDishId] = useState(scheduledUserDish.user_dish ? scheduledUserDish.user_dish.id : 0) - const [errorMessage, setErrorMessage] = useState("") - const [isSuccess, setIsSuccess] = useState(false); - - const selectStyle = classNames( - 'p-2', 'rounded', 'w-full', 'background-secondary', - 'focus:outline-none', - 'transition-[border-color] ease-out duration-1000', 'border-2', // Keep consistent base styles - { - 'border-green-500': isSuccess, // Green border when successful - 'border-red-500': !isSuccess && errorMessage !== "", // Red border when there's an error - 'border-secondary': !isSuccess && errorMessage === "", // Default border for neutral state - } - ) - - const handleOnChange = (e: FormEvent) => { - const userDishId = parseInt(e.currentTarget.value); - setSelectedUserDishId(userDishId); - - updateScheduledUserDish(scheduledUserDish.id, userDishId) - .then(() => { - setIsSuccess(false); - setTimeout(() => { - setIsSuccess(true); - setTimeout(() => setIsSuccess(false), 1000); - }, 0); - }) - .catch((error) => { - setErrorMessage(error); // Log API errors - }); - }; - - const filteredDishes = useMemo(() => - allDishes.filter((dish: DishType) => - dish.users.some((user) => user.id === scheduledUserDish.user_dish.user.id) - ), - [allDishes, scheduledUserDish.user_dish.user.id] - ) - - return ( -
-
{scheduledUserDish.user_dish.user.name}
- - { errorMessage !== "" && { errorMessage } } - - - -
- ); -}; - -export default UserDishEditCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx b/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx deleted file mode 100644 index 1338f51..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {DateTime} from "luxon"; -import React from "react"; -import classNames from "classnames"; - -interface Props { - date: string - className?: string; -} - -const DateBadge = ({ className, date }: Props) => { - const isToday = DateTime.fromISO(date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const textStyle = classNames("inline font-bold", { - 'text-accent-blue': isToday, - 'text-secondary': !isToday, - }, className) - - return ( -
-
{DateTime.fromISO(date).toFormat("dd")}
-
-
{DateTime.fromISO(date).toFormat("LLL")}
-
- ) -} - -export default DateBadge \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx b/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx deleted file mode 100644 index 8c6b874..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import {UserType} from "~/types/UserType"; -import ScheduleDayCardUserDish from "~/components/features/schedule/dayCard/ScheduleDayCardUserDish"; -import { FilledScheduleType, ScheduleType } from "~/types/ScheduleType"; -import { Link } from "react-router"; -import {PencilSquareIcon} from "@heroicons/react/24/outline"; -import useRoutes from "~/hooks/useRoutes"; -import DateBadge from "~/components/features/schedule/dayCard/DateBadge"; -import { DateTime } from "luxon" -import classNames from "classnames" - -interface Props { - schedule: ScheduleType|FilledScheduleType; - users: UserType[]; -} - -const ScheduleDayCard = ({schedule, users}: Props) => { - const routes = useRoutes() - const isToday = DateTime.fromISO(schedule.date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const containerStyles = classNames( - 'w-full bg-gray-500 pt-5 pb-2 rounded-2xl text-xl', { - 'border-2 text-accent-blue border-accent-blue': isToday, - } - ) - - return ( -
- - -
- { - users.map((user) => ) - } - -
- - Edit - -
-
-
- ); -}; - -export default ScheduleDayCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx b/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx deleted file mode 100644 index 35cf58d..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import { ScheduledUserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; - -interface Props { - schedule: ScheduleType|FilledScheduleType; - user: UserType; -} - -const ScheduleDayCardUserDish = ({ schedule, user }: Props) => { - const getDish = (user: UserType) => { - const scheduled_dishes = schedule.scheduled_user_dishes.filter((scheduled_user_dish: ScheduledUserDishType) => ( - scheduled_user_dish.user_dish?.user.id == user.id - )) - - if (scheduled_dishes.length > 0) { - return scheduled_dishes[0].user_dish.dish.name - } - - return '/' - } - - return ( -
-
{ user.name } :
-
{ getDish(user) }
-
- ); -}; - -export default ScheduleDayCardUserDish; \ No newline at end of file diff --git a/frontend-old/app/components/features/users/EditUserForm.tsx b/frontend-old/app/components/features/users/EditUserForm.tsx deleted file mode 100644 index e26cf58..0000000 --- a/frontend-old/app/components/features/users/EditUserForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {useState} from "react"; -import { useNavigate } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import {updateUser} from "~/utils/api/usersApi"; -import PageTitle from "~/components/ui/PageTitle"; -import { Link } from "react-router"; -import Alert from "~/components/ui/Alert"; -import {UserType} from "~/types/UserType"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; - -interface Props { - user: UserType; -} - -const EditUserForm = ({ user }: Props) => { - - const [name, setName] = useState(user.name); - const [error, setError] = useState(''); - const navigate = useNavigate(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - // validateName - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - updateUser(user, name) - .then(() => { - navigate(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Update -
-
- ); -} - -export default EditUserForm; \ No newline at end of file diff --git a/frontend-old/app/components/layout/AuthGuard.tsx b/frontend-old/app/components/layout/AuthGuard.tsx deleted file mode 100644 index 9f6cf29..0000000 --- a/frontend-old/app/components/layout/AuthGuard.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useAuth } from '@/context/AuthContext'; -import React, { useEffect } from 'react'; -import { useLocation, useNavigate } from "react-router" - -export default function AuthGuard({ children }: { children: React.ReactNode }) { - const { isAuthenticated } = useAuth(); - const navigate = useNavigate(); - const location = useLocation(); - - const publicRoutes = ['/login', '/register']; - const isPublic = publicRoutes.includes(location.pathname); - - useEffect(() => { - // Handle redirects based on auth state - if (isAuthenticated && isPublic) { - // Redirect authenticated users away from public pages - navigate('/', { replace: true }); - } else if (!isAuthenticated && !isPublic) { - // Redirect unauthenticated users trying to access protected pages - navigate('/login', { replace: true }); - } - }, [isAuthenticated, location.pathname, isPublic, navigate]); - - // Render children for all routes - redirects will happen via useEffect - return <>{children}; -} \ No newline at end of file diff --git a/frontend-old/app/components/layout/Card.tsx b/frontend-old/app/components/layout/Card.tsx deleted file mode 100644 index 4491cab..0000000 --- a/frontend-old/app/components/layout/Card.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; - -interface Props { - children: React.ReactNode; -} - -const Card = ({ children }: Props) => { - return ( -
- { children } -
- ) -} - -export default Card \ No newline at end of file diff --git a/frontend-old/app/components/layout/NavBar.tsx b/frontend-old/app/components/layout/NavBar.tsx deleted file mode 100644 index 05d71a9..0000000 --- a/frontend-old/app/components/layout/NavBar.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useState } from "react"; -import { Link, useNavigate } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import MobileDropdownMenu from "~/components/features/navbar/MobileDropdownMenu"; -import { useAuth } from "~/context/AuthContext"; - -const NavBar = () => { - const [isOpen, setIsOpen] = useState(false); - const routes = useRoutes(); - const navigate = useNavigate(); - const {isAuthenticated, logout} = useAuth(); - - const handleLogout = (e: React.MouseEvent) => { - e.preventDefault(); - logout(); - navigate('/login', { replace: true }); - }; - - return ( - - ); -}; - -export default NavBar; \ No newline at end of file diff --git a/frontend-old/app/components/pages/PrivatePage.tsx b/frontend-old/app/components/pages/PrivatePage.tsx deleted file mode 100644 index 3b08c86..0000000 --- a/frontend-old/app/components/pages/PrivatePage.tsx +++ /dev/null @@ -1,9 +0,0 @@ -const PrivatePage = () => { - return ( -
- private -
- ) -} - -export default PrivatePage \ No newline at end of file diff --git a/frontend-old/app/components/pages/PublicPage.tsx b/frontend-old/app/components/pages/PublicPage.tsx deleted file mode 100644 index 8e0f5cb..0000000 --- a/frontend-old/app/components/pages/PublicPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Outlet } from "react-router" - -const PublicPage = () => { - return ( -
- -
- ) -} - -export default PublicPage \ No newline at end of file diff --git a/frontend-old/app/components/ui/Alert.tsx b/frontend-old/app/components/ui/Alert.tsx deleted file mode 100644 index 9f08022..0000000 --- a/frontend-old/app/components/ui/Alert.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react" -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - type: 'error' | 'warning' | 'info' | 'success'; -} - -const Alert = ({ children, className, type }: Props) => { - let bgColor = 'bg-blue-200' - let fgColor = 'bg-blue-800' - - if (type == 'error') { - bgColor = 'bg-red-200' - fgColor = 'bg-red-800' - } else if (type == 'warning') { - bgColor = 'bg-orange-200' - fgColor = 'bg-orange-800' - } else if (type == 'success') { - bgColor = 'border-2 border-green-500' - fgColor = 'text-green-500' - } - - const styles = classNames(fgColor, bgColor, className, 'rounded') - - return ( -
- { children} -
- ) -} - -export default Alert \ No newline at end of file diff --git a/frontend-old/app/components/ui/Button.tsx b/frontend-old/app/components/ui/Button.tsx deleted file mode 100644 index 756e6b5..0000000 --- a/frontend-old/app/components/ui/Button.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Link } from "react-router"; -import React from "react"; -import classNames from "classnames"; - -interface ButtonProps { - appearance?: 'solid' | 'outline' | 'text'; - children: React.ReactNode; - className?: string; - href?: string; - icon?: React.ReactNode; - onClick?: () => void; - disabled?: boolean; - size?: 'small' | 'medium' | 'large'; - type?: 'button' | 'submit' | 'reset'; - variant?: 'primary' | 'secondary' | 'accent'; -} - -const Button = ({ appearance, children, className, disabled, href, icon, onClick, - size = 'medium', type, - variant = 'primary' -}: ButtonProps) => { - const styles = classNames( - "flex items-center space-x-1", - "justify-center font-size-18 py-2 px-4 rounded flex", - { - 'border-2 border-primary background-red text-white': variant === 'primary' && appearance === 'solid', - 'border-2 border-primary text-primary': variant === 'primary' && appearance === 'outline', - 'text-primary': variant === 'primary' && appearance === 'text', - 'border-2 border-secondary text-secondary': variant === 'secondary' && appearance === 'outline', - 'border-2 border-accent-blue text-accent-blue': variant === 'accent' && appearance === 'outline', - }, - className - ) - - const iconClassNames = classNames({ - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - if (href !== undefined) { - return ( - - { icon && iconElement} - { children} - - ) - } - - return -} - -export default Button \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/OutlineButton.tsx b/frontend-old/app/components/ui/Buttons/OutlineButton.tsx deleted file mode 100644 index 5530482..0000000 --- a/frontend-old/app/components/ui/Buttons/OutlineButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const OutlineButton = ({ children, className, disabled = false, onClick, size, type }: Props) => { - const style = classNames( - "justify-center border-2 border-accent font-size-18 text-accent-blue py-2 px-4 rounded flex", - { 'text-xs': size === "small" }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default OutlineButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx b/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx deleted file mode 100644 index 6032bc3..0000000 --- a/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import { Link } from "react-router" - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const OutlineLinkButton = ({ children, className, href, icon, size = "medium", variant }: Props) => { - const linkClassNames = classNames( - "underline font-default pt-3 pb-3 px-4 rounded mb-0 flex", - { - 'text-primary border-primary': variant === "primary", - 'text-secondary border-secondary': variant === "secondary", - 'text-accent-blue border-accent': !variant || !["primary", "secondary"].includes(variant), - }, { - 'text-size-14': size === "small", - 'font-size-18': !size || size === "medium", - 'text-2xl': size === "large", - }, - className, - ) - - const iconClassNames = classNames("mt-0.5", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - - {iconElement} - {children} - - ) -} - -export default OutlineLinkButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/SolidButton.tsx b/frontend-old/app/components/ui/Buttons/SolidButton.tsx deleted file mode 100644 index dffc021..0000000 --- a/frontend-old/app/components/ui/Buttons/SolidButton.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const SolidButton = ({ children, className, disabled = false, onClick, size, type }: Props) => { - const style = classNames( - "py-2 px-4 bg-primary text-white text-xl p-2 rounded hover:bg-secondary mb-0", - { - 'text-xs': size === "small", - 'font-size-18': !size || size === "medium", - }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default SolidButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx b/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx deleted file mode 100644 index ce4929c..0000000 --- a/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import { Link } from "react-router" - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const SolidLinkButton = ({ children, className, href, icon, size = "medium", variant }: Props) => { - const style = classNames( - "py-2 px-4 text-xl p-2 rounded hover:bg-secondary mb-0 text-center flex", - { - 'background-red text-white': variant === "primary", - 'background-secondary border-2 border-secondary': variant === "secondary", - }, - className - ) - - const iconClassNames = classNames("mt-1", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - -
- {iconElement} - {children} -
- - ) -} - -export default SolidLinkButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Description.tsx b/frontend-old/app/components/ui/Description.tsx deleted file mode 100644 index 8b429c0..0000000 --- a/frontend-old/app/components/ui/Description.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -interface Props { - children: React.ReactNode; - className?: string; -} - -const Description = ({ children, className }: Props) => { - const style = classNames("italic font-size-16", - className - ) - - return

{ children }

-} - -export default Description \ No newline at end of file diff --git a/frontend-old/app/components/ui/Hr.tsx b/frontend-old/app/components/ui/Hr.tsx deleted file mode 100644 index f1eee60..0000000 --- a/frontend-old/app/components/ui/Hr.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import classNames from "classnames" - -interface HrProps { - className?: string; -} - -const Hr = ({ className }: HrProps) => { - const styles = classNames("my-4 border-secondary", className) - - return
-} - -export default Hr \ No newline at end of file diff --git a/frontend-old/app/components/ui/Label.tsx b/frontend-old/app/components/ui/Label.tsx deleted file mode 100644 index a62d4e7..0000000 --- a/frontend-old/app/components/ui/Label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; - -interface LabelProps { - href?: string; - children: React.ReactNode; - onClick?: () => void; -} - -const Label = ({ href, children, onClick }: LabelProps) => { - const styles = "items-center space-x-1 background-accent p-2 rounded" - - if (href !== undefined) { - return ( -
- { children} -
- ) - } - - return -} - -export default Label \ No newline at end of file diff --git a/frontend-old/app/components/ui/Modal.tsx b/frontend-old/app/components/ui/Modal.tsx deleted file mode 100644 index e60fdb5..0000000 --- a/frontend-old/app/components/ui/Modal.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { type JSX } from "react"; -import classNames from "classnames"; -import Button from "@/components/ui/Button" - -interface ModalProps { - buttonChildren?: JSX.Element; - buttonClassName?: string; - buttonLabel?: string; - modalChildren: JSX.Element; - modalOpen?: boolean; - setModalOpen: (open: boolean) => void; -} - -const Modal = ({ - buttonLabel, - buttonClassName, - modalChildren, - modalOpen, - buttonChildren, - setModalOpen, -}: ModalProps) => { - const buttonStyles = classNames(buttonClassName, 'anta-regular'); - - const closeModal = () => { - setModalOpen(false) - } - - return ( - <> - - - {/**/} - {/* */} - {/*
*/} - {/* */} - {/* */} - {/* closeModal()}/>*/} - {/* {modalChildren}*/} - {/* */} - {/*
*/} - {/* */} - {/*
*/} - - ) -} - -export default Modal; \ No newline at end of file diff --git a/frontend-old/app/components/ui/PageTitle.tsx b/frontend-old/app/components/ui/PageTitle.tsx deleted file mode 100644 index 569b2f3..0000000 --- a/frontend-old/app/components/ui/PageTitle.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string, - className?: string, -} - -const PageTitle = ({ children, className }: Props) => { - const styles = classNames( - 'ml-4 text-2xl font-default uppercase w-full text-accent-blue font-bold', - className, - ) - - return

{ children }

-} - -export default PageTitle \ No newline at end of file diff --git a/frontend-old/app/components/ui/RecurrenceInput.tsx b/frontend-old/app/components/ui/RecurrenceInput.tsx deleted file mode 100644 index 0d82560..0000000 --- a/frontend-old/app/components/ui/RecurrenceInput.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, {useState} from "react"; - -interface Props { - value: number; - setValue: (value: number) => void; -} - -const RecurrenceInput = ({ value, setValue}: Props) => { - const [openInput, setOpenInput] = useState<'category' | 'number'>([7, 365].includes(value) ? 'category' : 'number') - - const toggleInput = (e: React.MouseEvent) => { - e.preventDefault() - setOpenInput(openInput == 'category' ? 'number' : 'category') - } - - const toggleButton = () => { - return ( - - ) - } - - const prepareValue = (v: string) => { - setValue(parseInt(v)) - } - - return ( -
-
- - - { toggleButton() } -
- -
- - prepareValue(e.target.value)} - className="p-2 border rounded w-full bg-gray-500 border-secondary" - /> - { toggleButton() } -
-
- ) -} - -export default RecurrenceInput \ No newline at end of file diff --git a/frontend-old/app/components/ui/SectionTitle.tsx b/frontend-old/app/components/ui/SectionTitle.tsx deleted file mode 100644 index 93d13c4..0000000 --- a/frontend-old/app/components/ui/SectionTitle.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string; - className?: string; -} - -const SectionTitle = ({ children, className }: Props) => { - const style = classNames("block font-size-18 uppercase w-full pl-2 text-accent-blue", - className - ) - - return

{ children }

-} - -export default SectionTitle \ No newline at end of file diff --git a/frontend-old/app/components/ui/Toggle.tsx b/frontend-old/app/components/ui/Toggle.tsx deleted file mode 100644 index 0d6b9e9..0000000 --- a/frontend-old/app/components/ui/Toggle.tsx +++ /dev/null @@ -1,41 +0,0 @@ - -interface ToggleProps { - checked: boolean; - onChange: (checked: boolean) => void; -} - -const Toggle = ({ checked, onChange }: ToggleProps) => { - const handleChange = () => { - onChange(checked); - } - - return ( - - ); -}; - -export default Toggle; \ No newline at end of file diff --git a/frontend-old/app/context/AuthContext.tsx b/frontend-old/app/context/AuthContext.tsx deleted file mode 100644 index 1cd3db1..0000000 --- a/frontend-old/app/context/AuthContext.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { createContext, useContext, useEffect, useState } from 'react'; - -interface AuthContextProps { - isAuthenticated: boolean; - login: () => void; - logout: () => void; -} - -const AuthContext = createContext({ - isAuthenticated: false, - login: () => {}, - logout: () => {}, -}); - -export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - // Start with false during SSR, will be updated on client - const [isAuthenticated, setIsAuthenticated] = useState(false); - - useEffect(() => { - // Check token on client after mount - const token = localStorage.getItem('token'); - setIsAuthenticated(!!token); - }, []); - - const login = () => { - setIsAuthenticated(true); - }; - - const logout = () => { - setIsAuthenticated(false); - localStorage.removeItem('token'); - }; - - return ( - - {children} - - ); -}; - -export const useAuth = () => useContext(AuthContext); \ No newline at end of file diff --git a/frontend-old/app/helpers/Date.ts b/frontend-old/app/helpers/Date.ts deleted file mode 100644 index f183c86..0000000 --- a/frontend-old/app/helpers/Date.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DateTime } from 'luxon'; - -// Validate if a given string matches the "yyyy-MM-dd" format and is a valid date -export const isValidDate = (date: string): boolean => { - const parsedDate = DateTime.fromFormat(date, 'yyyy-MM-dd'); - return parsedDate.isValid && parsedDate.toFormat('yyyy-MM-dd') === date; -}; - -// Format a date to a specific string format -export const formatDate = (date: Date | string, format: string = 'yyyy-MM-dd'): string => { - const parsedDate = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date); - return parsedDate.toFormat(format); -}; - -// Compare two dates to see if one is before the other -export const isBefore = (date1: string, date2: string): boolean => { - return DateTime.fromISO(date1) < DateTime.fromISO(date2); -}; diff --git a/frontend-old/app/hooks/useFetchDishes.ts b/frontend-old/app/hooks/useFetchDishes.ts deleted file mode 100644 index a7ab747..0000000 --- a/frontend-old/app/hooks/useFetchDishes.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import { listDishes } from "@/utils/api/dishApi" -import { DishType } from "@/types/DishType" - -export const useFetchDishes = () => { - const [dishes, setDishes] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchDishes = async () => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchDishes(); - }, []); - - return { dishes, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/app/hooks/useFetchUsers.ts b/frontend-old/app/hooks/useFetchUsers.ts deleted file mode 100644 index f6df05b..0000000 --- a/frontend-old/app/hooks/useFetchUsers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import {UserType} from "@/types/UserType"; -import {listUsers} from "@/utils/api/usersApi"; - -export const useFetchUsers = () => { - const [users, setUsers] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchUsers = async () => { - listUsers() - .then((users: UserType[]) => setUsers(users)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchUsers(); - }, []); - - return { users, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/app/hooks/useRoutes.ts b/frontend-old/app/hooks/useRoutes.ts deleted file mode 100644 index 255a545..0000000 --- a/frontend-old/app/hooks/useRoutes.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { DishType } from "@/types/DishType"; -import type { UserType } from "@/types/UserType"; - -const useRoutes = () => { - return { - home: () => "/", - auth: { - login: () => "/login", - register: () => "/register", - }, - dish: { - index: () => "/dishes", - create: () => "/dishes/create", - edit: (dish: DishType) => `/dishes/${ dish.id }/edit`, - delete: (dish: DishType) => `/dishes/${ dish.id }/delete`, - }, - schedule: { - date: { - edit: (date: string) => `/schedule/${ date }/edit` - }, - history: () => "/scheduled-user-dishes/history", - }, - user: { - index: () => "/users", - create: () => `/users/create`, - edit: (user: UserType) => `/users/${ user.id }/edit`, - delete: (user: UserType) => `/users/${ user.id }/delete`, - } - }; -}; - -export default useRoutes; \ No newline at end of file diff --git a/frontend-old/app/root.tsx b/frontend-old/app/root.tsx deleted file mode 100644 index baa343e..0000000 --- a/frontend-old/app/root.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { - isRouteErrorResponse, - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, -} from "react-router"; - -import type { Route } from "./+types/root"; -import "./app.css"; -import React from "react" -import { AuthProvider } from "~/context/AuthContext" -import AuthGuard from "~/components/layout/AuthGuard" -import NavBar from "~/components/layout/NavBar" - -export const links: Route.LinksFunction = () => [ - { rel: "preconnect", href: "https://fonts.googleapis.com" }, - { - rel: "preconnect", - href: "https://fonts.gstatic.com", - crossOrigin: "anonymous", - }, - { - rel: "stylesheet", - href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", - }, -]; - -export function Layout({ children }: { children: React.ReactNode }) { - return ( - - - - - - - - - - - -
{ children }
-
-
- - - - - ); -} - -export default function App() { - return ; -} - -export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { - let message = "Oops!"; - let details = "An unexpected error occurred."; - let stack: string | undefined; - - if (isRouteErrorResponse(error)) { - message = error.status === 404 ? "404" : "Error"; - details = - error.status === 404 - ? "The requested page could not be found." - : error.statusText || details; - } else if (import.meta.env.DEV && error && error instanceof Error) { - details = error.message; - stack = error.stack; - } - - return ( -
-

{ message }

-

{ details }

- { stack && ( -
-          { stack }
-        
- ) } -
- ); -} diff --git a/frontend-old/app/routes.ts b/frontend-old/app/routes.ts deleted file mode 100644 index 8a4afe8..0000000 --- a/frontend-old/app/routes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { type RouteConfig, index, route } from "@react-router/dev/routes"; - -export default [ - index("routes/home.tsx"), - route("login", "./components/features/auth/LoginForm.tsx"), - route("register", "./components/features/auth/RegisterForm.tsx"), - - // Dishes routes - route("dishes", "routes/dishes.tsx"), - route("dishes/create", "routes/dishes.create.tsx"), - route("dishes/:id/edit", "routes/dishes.$id.edit.tsx"), - - // Users routes - route("users", "routes/users.tsx"), - route("users/create", "routes/users.create.tsx"), - route("users/:id/edit", "routes/users.$id.edit.tsx"), - - // Schedule routes - route("schedule/:date/edit", "routes/schedule.$date.edit.tsx"), - - // History route - route("scheduled-user-dishes/history", "routes/scheduled-user-dishes.history.tsx"), -] satisfies RouteConfig; diff --git a/frontend-old/app/routes/dishes.$id.edit.tsx b/frontend-old/app/routes/dishes.$id.edit.tsx deleted file mode 100644 index a8a3fa0..0000000 --- a/frontend-old/app/routes/dishes.$id.edit.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { Route } from "./+types/dishes.$id.edit"; -import { useCallback, useEffect, useState } from "react"; -import PageTitle from "~/components/ui/PageTitle"; -import EditDishForm from "~/components/features/dishes/EditDishForm"; -import { DishType } from "~/types/DishType"; -import Spinner from "~/components/Spinner"; -import { fetchDish } from "~/utils/api/dishApi"; -import SyncUsersForm from "~/components/features/dishes/SyncUsersForm"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import useRoutes from "~/hooks/useRoutes"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; -import Hr from "~/components/ui/Hr"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit Dish" }, - { name: "description", content: "Edit dish details" }, - ]; -} - -export default function EditDishPage() { - const params = useParams(); - const id = Number(params.id); - const [dish, setDish] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const routes = useRoutes(); - - const loadDish = useCallback(async () => { - try { - const fetchedDish = await fetchDish(id); - setDish(fetchedDish); - } catch (error) { - console.error("Error fetching dish:", error); - throw new Error("No token found in localStorage."); - } finally { - setIsLoading(false); - } - }, [id]); - - useEffect(() => { - loadDish(); - }, [loadDish]); - - if (isLoading || dish === null) { - return ; - } - - return ( -
-
- Edit Dish - - -

BACK

-
-
- - - -
- - -
- ); -} diff --git a/frontend-old/app/routes/dishes.create.tsx b/frontend-old/app/routes/dishes.create.tsx deleted file mode 100644 index a43e88d..0000000 --- a/frontend-old/app/routes/dishes.create.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/dishes.create"; -import CreateDishForm from "~/components/features/dishes/CreateDishForm"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Create Dish" }, - { name: "description", content: "Create a new dish" }, - ]; -} - -export default function CreateDishPage() { - return ; -} diff --git a/frontend-old/app/routes/dishes.tsx b/frontend-old/app/routes/dishes.tsx deleted file mode 100644 index 7bed7de..0000000 --- a/frontend-old/app/routes/dishes.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import type { Route } from "./+types/dishes"; -import PageTitle from "~/components/ui/PageTitle"; -import { DishType } from "~/types/DishType"; -import Dish from "~/components/features/dishes/Dish"; -import { PlusIcon } from "@heroicons/react/24/solid"; -import { useEffect, useState } from "react"; -import useRoutes from "~/hooks/useRoutes"; -import { listDishes } from "~/utils/api/dishApi"; -import Button from "~/components/ui/Button"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Dishes" }, - { name: "description", content: "Manage your dishes" }, - ]; -} - -export default function DishesIndexPage() { - const routes = useRoutes(); - - const [dishes, setDishes] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .finally(() => setLoading(false)); - }, []); - - if (loading) return

Loading...

; - - if (!dishes) { - return

Loading...

; - } - - return ( - <> -
-
- Dishes -
-
- -
-
- - {dishes.length === 0 ? ( -

No dishes found :(

- ) : ( - dishes.map((dish: DishType, index: number) => ( - - )) - )} - - ); -} diff --git a/frontend-old/app/routes/home.tsx b/frontend-old/app/routes/home.tsx deleted file mode 100644 index a1c6ee6..0000000 --- a/frontend-old/app/routes/home.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/home"; -import UpcomingDishes from "~/components/features/schedule/UpcomingDishes"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Schedule" }, - { name: "description", content: "View and manage your upcoming dish schedule" }, - ]; -} - -export default function Home() { - return ; -} diff --git a/frontend-old/app/routes/schedule.$date.edit.tsx b/frontend-old/app/routes/schedule.$date.edit.tsx deleted file mode 100644 index 4122f13..0000000 --- a/frontend-old/app/routes/schedule.$date.edit.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Route } from "./+types/schedule.$date.edit"; -import ScheduleEditForm from "~/components/features/schedule/ScheduleEditForm"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit Schedule" }, - { name: "description", content: "Edit schedule for a specific date" }, - ]; -} - -const ScheduleEditPage = () => { - const params = useParams(); - const date = params.date as string; - - return ; -}; - -export default ScheduleEditPage; diff --git a/frontend-old/app/routes/scheduled-user-dishes.history.tsx b/frontend-old/app/routes/scheduled-user-dishes.history.tsx deleted file mode 100644 index 591f015..0000000 --- a/frontend-old/app/routes/scheduled-user-dishes.history.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/scheduled-user-dishes.history"; -import HistoricalDishes from "~/components/features/schedule/HistoricalDishes"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - History" }, - { name: "description", content: "View historical dishes" }, - ]; -} - -export default function HistoryPage() { - return ; -} diff --git a/frontend-old/app/routes/users.$id.edit.tsx b/frontend-old/app/routes/users.$id.edit.tsx deleted file mode 100644 index 2b784ed..0000000 --- a/frontend-old/app/routes/users.$id.edit.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { Route } from "./+types/users.$id.edit"; -import { useEffect, useState } from "react"; -import { UserType } from "~/types/UserType"; -import { showUser } from "~/utils/api/usersApi"; -import Spinner from "~/components/Spinner"; -import EditUserForm from "~/components/features/users/EditUserForm"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit User" }, - { name: "description", content: "Edit user details" }, - ]; -} - -const UpdateUsersPage = () => { - const params = useParams(); - const id = Number(params.id); - const [user, setUser] = useState(null); - - useEffect(() => { - showUser(id).then((user: UserType) => setUser(user)); - }, [id]); - - if (!user) { - return ; - } - - return ; -}; - -export default UpdateUsersPage; diff --git a/frontend-old/app/routes/users.create.tsx b/frontend-old/app/routes/users.create.tsx deleted file mode 100644 index 34feb54..0000000 --- a/frontend-old/app/routes/users.create.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { Route } from "./+types/users.create"; -import PageTitle from "~/components/ui/PageTitle"; -import useRoutes from "~/hooks/useRoutes"; -import { useNavigate } from "react-router"; -import { useState } from "react"; -import Alert from "~/components/ui/Alert"; -import { createUser } from "~/utils/api/usersApi"; -import { Link } from "react-router"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Create User" }, - { name: "description", content: "Create a new user" }, - ]; -} - -const CreateUsersPage = () => { - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const navigate = useNavigate(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!name.trim()) { - setError("Name cannot be empty."); - return; - } - - createUser(name).then(() => { - navigate(routes.user.index()); - }); - }; - - return ( -
- Create User - - Back to users - - -
- {error != "" && ( - - {error} - - )} - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - - Create - -
-
- ); -}; - -export default CreateUsersPage; diff --git a/frontend-old/app/routes/users.tsx b/frontend-old/app/routes/users.tsx deleted file mode 100644 index a8a6039..0000000 --- a/frontend-old/app/routes/users.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { Route } from "./+types/users"; -import PageTitle from "~/components/ui/PageTitle"; -import { useFetchUsers } from "~/hooks/useFetchUsers"; -import Spinner from "~/components/Spinner"; -import useRoutes from "~/hooks/useRoutes"; -import { Link } from "react-router"; -import { PencilIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid"; -import React from "react"; -import { deleteUser } from "~/utils/api/usersApi"; -import { UserType } from "~/types/UserType"; -import Card from "~/components/layout/Card"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Users" }, - { name: "description", content: "Manage your users" }, - ]; -} - -const UsersPage = () => { - const { users, isLoading } = useFetchUsers(); - const routes = useRoutes(); - - const handleDelete = (user: UserType) => { - deleteUser(user).then(() => window.location.reload()); - }; - - if (isLoading) { - return ; - } - - const usersList = () => { - return users.map((user) => ( - -
{user.name}
-
-
- -
- -
- -
-
- handleDelete(user)}> -
- -
- -
-
-
- )); - }; - - return ( -
-
-
- Users -
- -
- - -

Add User

-
-
-
- - {users && users.length > 0 ? usersList() :
No users
} -
- ); -}; - -export default UsersPage; diff --git a/frontend-old/app/styles/base/globals.css b/frontend-old/app/styles/base/globals.css deleted file mode 100644 index 918cbfb..0000000 --- a/frontend-old/app/styles/base/globals.css +++ /dev/null @@ -1,19 +0,0 @@ -html, body { - margin: 0; - padding: 0; - width: 100%; - overflow-x: hidden; -} - -body { - font-family: Arial, Helvetica, sans-serif; -} - - -.toggle-input:checked { - background-color: #22c55e; /* bg-green-500 */ -} - -.toggle-input:checked ~ span:last-child { - --tw-translate-x: 1.75rem; /* translate-x-7 */ -} \ No newline at end of file diff --git a/frontend-old/app/styles/components/buttons.css b/frontend-old/app/styles/components/buttons.css deleted file mode 100644 index 07fc4de..0000000 --- a/frontend-old/app/styles/components/buttons.css +++ /dev/null @@ -1,42 +0,0 @@ -.button-primary-solid { - background-color: var(--color-primary); - color: var(--color-secondary-200); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} -.button-primary-outline { - background-color: var(--color-background); - color: var(--color-primary); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} - -.button-secondary-solid { - background-color: var(--color-secondary); - color: var(--color-primary); - border: 1px solid var(--color-secondary); -} - -.button-accent-solid { - background-color: var(--color-accent-blue); - color: var(--color-secondary-900); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline { - background-color: var(--color-background); - color: var(--color-accent-blue); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline:hover { - background-color: var(--color-background-400); -} \ No newline at end of file diff --git a/frontend-old/app/styles/components/select.css b/frontend-old/app/styles/components/select.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend-old/app/styles/main.css b/frontend-old/app/styles/main.css deleted file mode 100644 index 83d03cc..0000000 --- a/frontend-old/app/styles/main.css +++ /dev/null @@ -1,6 +0,0 @@ -@import "./theme/borders.css"; -@import "./theme/fonts.css"; -@import "./components/buttons.css"; - -@import "./base/globals.css"; -@import "./theme/colors.css"; diff --git a/frontend-old/app/styles/theme/borders.css b/frontend-old/app/styles/theme/borders.css deleted file mode 100644 index b4ed5db..0000000 --- a/frontend-old/app/styles/theme/borders.css +++ /dev/null @@ -1,14 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} -.border-accent-800 { - border-color: var(--color-accent-blue-800); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors.css b/frontend-old/app/styles/theme/colors.css deleted file mode 100644 index f283897..0000000 --- a/frontend-old/app/styles/theme/colors.css +++ /dev/null @@ -1,10 +0,0 @@ -@import './colors/root.css'; -@import './colors/background.css'; -@import './colors/border.css'; -@import './colors/text.css'; - -body { - color: var(--color-secondary) !important; - background: var(--color-gray-600) !important; -} - diff --git a/frontend-old/app/styles/theme/colors/background.css b/frontend-old/app/styles/theme/colors/background.css deleted file mode 100644 index e8548f5..0000000 --- a/frontend-old/app/styles/theme/colors/background.css +++ /dev/null @@ -1,226 +0,0 @@ -.bg-gray-100 { - background-color: var(--color-gray-100) !important; -} -.bg-gray-200 { - background-color: var(--color-gray-200) !important; -} -.bg-gray-300 { - background-color: var(--color-gray-300) !important; -} -.bg-gray-400 { - background-color: var(--color-gray-400) !important; -} -.bg-gray-500 { - background-color: var(--color-gray-500) !important; -} -.bg-gray-600 { - background-color: var(--color-gray-600) !important; -} -.bg-gray-700 { - background-color: var(--color-gray-700) !important; -} -.bg-gray-800 { - background-color: var(--color-gray-800) !important; -} -.bg-gray-900 { - background-color: var(--color-gray-900) !important; -} - - -.bg-primary { - background-color: var(--color-primary) !important; -} - - -.bg-accent-blue { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-100 { - background-color: var(--color-accent-blue-100) !important; -} -.bg-accent-blue-200 { - background-color: var(--color-accent-blue-200) !important; -} -.bg-accent-blue-300 { - background-color: var(--color-accent-blue-300) !important; -} -.bg-accent-blue-400 { - background-color: var(--color-accent-blue-400) !important; -} -.bg-accent-blue-500 { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-600 { - background-color: var(--color-accent-blue-600) !important; -} -.bg-accent-blue-700 { - background-color: var(--color-accent-blue-700) !important; -} -.bg-accent-blue-800 { - background-color: var(--color-accent-blue-800) !important; -} -.bg-accent-blue-900 { - background-color: var(--color-accent-blue-900) !important; -} - - -.bg-accent-yellow { - background-color: var(--color-accent-yellow) !important; -} - -.bg-accent-yellow-100 { - background-color: var(--color-accent-yellow-100) !important; -} - -.bg-accent-yellow-200 { - background-color: var(--color-accent-yellow-200) !important; -} - -.bg-accent-yellow-300 { - background-color: var(--color-accent-yellow-300) !important; -} - -.bg-accent-yellow-400 { - background-color: var(--color-accent-yellow-400) !important; -} - -.bg-accent-yellow-500 { - background-color: var(--color-accent-yellow-500) !important; -} - -.bg-accent-yellow-600 { - background-color: var(--color-accent-yellow-600) !important; -} - -.bg-accent-yellow-700 { - background-color: var(--color-accent-yellow-700) !important; -} - -.bg-accent-yellow-800 { - background-color: var(--color-accent-yellow-800) !important; -} - -.bg-accent-yellow-900 { - background-color: var(--color-accent-yellow-900) !important; -} - - -.bg-success { - background-color: var(--color-success) !important; -} - -.bg-success-100 { - background-color: var(--color-success-100) !important; -} - -.bg-success-200 { - background-color: var(--color-success-200) !important; -} - -.bg-success-300 { - background-color: var(--color-success-300) !important; -} - -.bg-success-400 { - background-color: var(--color-success-400) !important; -} - -.bg-success-500 { - background-color: var(--color-success-500) !important; -} - -.bg-success-600 { - background-color: var(--color-success-600) !important; -} - -.bg-success-700 { - background-color: var(--color-success-700) !important; -} - -.bg-success-800 { - background-color: var(--color-success-800) !important; -} - -.bg-success-900 { - background-color: var(--color-success-900) !important; -} - -.bg-warning { - background-color: var(--color-warning) !important; -} - -.bg-warning-100 { - background-color: var(--color-warning-100) !important; -} - -.bg-warning-200 { - background-color: var(--color-warning-200) !important; -} - -.bg-warning-300 { - background-color: var(--color-warning-300) !important; -} - -.bg-warning-400 { - background-color: var(--color-warning-400) !important; -} - -.bg-warning-500 { - background-color: var(--color-warning-500) !important; -} - -.bg-warning-600 { - background-color: var(--color-warning-600) !important; -} - -.bg-warning-700 { - background-color: var(--color-warning-700) !important; -} - -.bg-warning-800 { - background-color: var(--color-warning-800) !important; -} - -.bg-warning-900 { - background-color: var(--color-warning-900) !important; -} - -.bg-danger { - background-color: var(--color-danger) !important; -} - -.bg-danger-100 { - background-color: var(--color-danger-100) !important; -} - -.bg-danger-200 { - background-color: var(--color-danger-200) !important; -} - -.bg-danger-300 { - background-color: var(--color-danger-300) !important; -} - -.bg-danger-400 { - background-color: var(--color-danger-400) !important; -} - -.bg-danger-500 { - background-color: var(--color-danger-500) !important; -} - -.bg-danger-600 { - background-color: var(--color-danger-600) !important; -} - -.bg-danger-700 { - background-color: var(--color-danger-700) !important; -} - -.bg-danger-800 { - background-color: var(--color-danger-800) !important; -} - -.bg-danger-900 { - background-color: var(--color-danger-900) !important; -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors/border.css b/frontend-old/app/styles/theme/colors/border.css deleted file mode 100644 index 8975296..0000000 --- a/frontend-old/app/styles/theme/colors/border.css +++ /dev/null @@ -1,286 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-primary-100 { - border-color: var(--color-primary-100); -} - -.border-primary-200 { - border-color: var(--color-primary-200); -} - -.border-primary-300 { - border-color: var(--color-primary-300); -} - -.border-primary-400 { - border-color: var(--color-primary-400); -} - -.border-primary-500 { - border-color: var(--color-primary-500); -} - -.border-primary-600 { - border-color: var(--color-primary-600); -} - -.border-primary-700 { - border-color: var(--color-primary-700); -} - -.border-primary-800 { - border-color: var(--color-primary-800); -} - -.border-primary-900 { - border-color: var(--color-primary-900); -} - - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-secondary-100 { - border-color: var(--color-secondary-100); -} - -.border-secondary-200 { - border-color: var(--color-secondary-200); -} - -.border-secondary-300 { - border-color: var(--color-secondary-300); -} - -.border-secondary-400 { - border-color: var(--color-secondary-400); -} - -.border-secondary-500 { - border-color: var(--color-secondary-500); -} - -.border-secondary-600 { - border-color: var(--color-secondary-600); -} - -.border-secondary-700 { - border-color: var(--color-secondary-700); -} - -.border-secondary-800 { - border-color: var(--color-secondary-800); -} - -.border-secondary-900 { - border-color: var(--color-secondary-900); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} - -.border-accent-blue-100 { - border-color: var(--color-accent-blue-100); -} - -.border-accent-blue-200 { - border-color: var(--color-accent-blue-200); -} - -.border-accent-blue-300 { - border-color: var(--color-accent-blue-300); -} - -.border-accent-blue-400 { - border-color: var(--color-accent-blue-400); -} - -.border-accent-blue-500 { - border-color: var(--color-accent-blue-500); -} - -.border-accent-blue-600 { - border-color: var(--color-accent-blue-600); -} - -.border-accent-blue-700 { - border-color: var(--color-accent-blue-700); -} - -.border-accent-blue-800 { - border-color: var(--color-accent-blue-800); -} - -.border-accent-blue-900 { - border-color: var(--color-accent-blue-900); -} - - -.border-accent-yellow { - border-color: var(--color-accent-yellow); -} - -.border-accent-yellow-100 { - border-color: var(--color-accent-yellow-100); -} - -.border-accent-yellow-200 { - border-color: var(--color-accent-yellow-200); -} - -.border-accent-yellow-300 { - border-color: var(--color-accent-yellow-300); -} - -.border-accent-yellow-400 { - border-color: var(--color-accent-yellow-400); -} - -.border-accent-yellow-500 { - border-color: var(--color-accent-yellow-500); -} - -.border-accent-yellow-600 { - border-color: var(--color-accent-yellow-600); -} - -.border-accent-yellow-700 { - border-color: var(--color-accent-yellow-700); -} - -.border-accent-yellow-800 { - border-color: var(--color-accent-yellow-800); -} - -.border-accent-yellow-900 { - border-color: var(--color-accent-yellow-900); -} - - -.border-background { - border-color: var(--color-background) !important; -} - -.border-danger { - border-color: var(--color-danger); -} - -.border-danger-100 { - border-color: var(--color-danger-100); -} - -.border-danger-200 { - border-color: var(--color-danger-200); -} - -.border-danger-300 { - border-color: var(--color-danger-300); -} - -.border-danger-400 { - border-color: var(--color-danger-400); -} - -.border-danger-500 { - border-color: var(--color-danger-500); -} - -.border-danger-600 { - border-color: var(--color-danger-600); -} - -.border-danger-700 { - border-color: var(--color-danger-700); -} - -.border-danger-800 { - border-color: var(--color-danger-800); -} - -.border-danger-900 { - border-color: var(--color-danger-900); -} - -.border-success { - border-color: var(--color-success); -} - -.border-success-100 { - border-color: var(--color-success-100); -} - -.border-success-200 { - border-color: var(--color-success-200); -} - -.border-success-300 { - border-color: var(--color-success-300); -} - -.border-success-400 { - border-color: var(--color-success-400); -} - -.border-success-500 { - border-color: var(--color-success-500); -} - -.border-success-600 { - border-color: var(--color-success-600); -} - -.border-success-700 { - border-color: var(--color-success-700); -} - -.border-success-800 { - border-color: var(--color-success-800); -} - -.border-success-900 { - border-color: var(--color-success-900); -} - -.border-warning { - border-color: var(--color-warning); -} - -.border-warning-100 { - border-color: var(--color-warning-100); -} - -.border-warning-200 { - border-color: var(--color-warning-200); -} - -.border-warning-300 { - border-color: var(--color-warning-300); -} - -.border-warning-400 { - border-color: var(--color-warning-400); -} - -.border-warning-500 { - border-color: var(--color-warning-500); -} - -.border-warning-600 { - border-color: var(--color-warning-600); -} - -.border-warning-700 { - border-color: var(--color-warning-700); -} - -.border-warning-800 { - border-color: var(--color-warning-800); -} - -.border-warning-900 { - border-color: var(--color-warning-900); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors/root.css b/frontend-old/app/styles/theme/colors/root.css deleted file mode 100644 index acffac0..0000000 --- a/frontend-old/app/styles/theme/colors/root.css +++ /dev/null @@ -1,193 +0,0 @@ -:root { - --color-rose-50: #FFF5FC; - --color-rose-100: #FCE6F5; - --color-rose-200: #FAC3E7; - --color-rose-300: #F7A1D5; - --color-rose-400: #F25EAB; - --color-rose-500: #ED1F79; - --color-rose-600: #D61A68; - --color-rose-700: #B3124F; - --color-rose-800: #8F0B39; - --color-rose-900: #6B0626; - --color-rose-950: #450315; - - --color-deluge-50: #FAF7FC; - --color-deluge-100: #F2EDF7; - --color-deluge-200: #E2DAF0; - --color-deluge-300: #CEC3E6; - --color-deluge-400: #A49BD1; - --color-deluge-500: #7776BC; - --color-deluge-600: #6361AB; - --color-deluge-700: #43428C; - --color-deluge-800: #2C2B70; - --color-deluge-900: #191854; - --color-deluge-950: #0A0A36; - - --color-malibu-50: #FAFEFF; - --color-malibu-100: #F5FDFF; - --color-malibu-200: #E1F6FC; - --color-malibu-300: #CDEDFA; - --color-malibu-400: #ABDEF7; - --color-malibu-500: #85C7F2; - --color-malibu-600: #6EACDB; - --color-malibu-700: #4A81B5; - --color-malibu-800: #305F91; - --color-malibu-900: #1B3F6E; - --color-malibu-950: #0B2247; - - --color-gamboge-50: #FFFDF2; - --color-gamboge-100: #FCF7E3; - --color-gamboge-200: #FAECBB; - --color-gamboge-300: #F5DC93; - --color-gamboge-400: #EDBB47; - --color-gamboge-500: #E59500; - --color-gamboge-600: #CF7F00; - --color-gamboge-700: #AB6100; - --color-gamboge-800: #8A4700; - --color-gamboge-900: #663000; - --color-gamboge-950: #421C00; - - --color-ebony-clay-100: #9AA2B3; /* Soft slate */ - --color-ebony-clay-200: #7A8093; /* Balanced midtone */ - --color-ebony-clay-300: #5D637A; /* Former 400 */ - --color-ebony-clay-400: #444760; /* New shadowed steel */ - --color-ebony-clay-500: #2B2C41; - --color-ebony-clay-600: #24263C; /* Adjusted — less jumpy */ - --color-ebony-clay-700: #1D1E36; /* Interpolated midpoint */ - --color-ebony-clay-800: #131427; /* Slightly lifted from old 800 */ - --color-ebony-clay-900: #0A0B1C; - --color-ebony-clay-950: #030412; - - --color-alizarin-crimson-50: #FFF5FA; - --color-alizarin-crimson-100: #FCE6F1; - --color-alizarin-crimson-200: #FAC3DC; - --color-alizarin-crimson-300: #F59FC0; - --color-alizarin-crimson-400: #F05D82; - --color-alizarin-crimson-500: #E71D36; - --color-alizarin-crimson-600: #D1192F; - --color-alizarin-crimson-700: #AD1121; - --color-alizarin-crimson-800: #8C0B18; - --color-alizarin-crimson-900: #69060E; - --color-alizarin-crimson-950: #420308; - - --color-spring-green-50: #F5FFFC; - --color-spring-green-100: #E8FFF9; - --color-spring-green-200: #C7FFEE; - --color-spring-green-300: #A4FCDF; - --color-spring-green-400: #62FCBC; - --color-spring-green-500: #21FA90; - --color-spring-green-600: #1BE07A; - --color-spring-green-700: #13BA5E; - --color-spring-green-800: #0C9646; - --color-spring-green-900: #07702D; - --color-spring-green-950: #03471A; - - --color-burning-orange-50: #FFFBF5; - --color-burning-orange-100: #FFF7EB; - --color-burning-orange-200: #FFE8CC; - --color-burning-orange-300: #FFD5AD; - --color-burning-orange-400: #FFA973; - --color-burning-orange-500: #FF6B35; - --color-burning-orange-600: #E65A2C; - --color-burning-orange-700: #BF441F; - --color-burning-orange-800: #993114; - --color-burning-orange-900: #731F0A; - --color-burning-orange-950: #4A1004; - - /* Standard naming */ - - --color-primary: var(--color-rose-500); - --color-primary-100: var(--color-rose-100); - --color-primary-200: var(--color-rose-200); - --color-primary-300: var(--color-rose-300); - --color-primary-400: var(--color-rose-400); - --color-primary-500: var(--color-rose-500); - --color-primary-600: var(--color-rose-600); - --color-primary-700: var(--color-rose-700); - --color-primary-800: var(--color-rose-800); - --color-primary-900: var(--color-rose-900); - - --color-secondary: var(--color-deluge-500); - --color-secondary-100: var(--color-deluge-100); - --color-secondary-200: var(--color-deluge-200); - --color-secondary-300: var(--color-deluge-300); - --color-secondary-400: var(--color-deluge-400); - --color-secondary-500: var(--color-deluge-500); - --color-secondary-600: var(--color-deluge-600); - --color-secondary-700: var(--color-deluge-700); - --color-secondary-800: var(--color-deluge-800); - --color-secondary-900: var(--color-deluge-900); - - --color-accent-blue: var(--color-malibu-500); - --color-accent-blue-100: var(--color-malibu-100); - --color-accent-blue-200: var(--color-malibu-200); - --color-accent-blue-300: var(--color-malibu-300); - --color-accent-blue-400: var(--color-malibu-400); - --color-accent-blue-500: var(--color-malibu-500); - --color-accent-blue-600: var(--color-malibu-600); - --color-accent-blue-700: var(--color-malibu-700); - --color-accent-blue-800: var(--color-malibu-800); - --color-accent-blue-900: var(--color-malibu-900); - - --color-accent-yellow: var(--color-gamboge-500); - --color-accent-yellow-50: var(--color-gamboge-50); - --color-accent-yellow-100: var(--color-gamboge-100); - --color-accent-yellow-200: var(--color-gamboge-200); - --color-accent-yellow-300: var(--color-gamboge-300); - --color-accent-yellow-400: var(--color-gamboge-400); - --color-accent-yellow-500: var(--color-gamboge-500); - --color-accent-yellow-600: var(--color-gamboge-600); - --color-accent-yellow-700: var(--color-gamboge-700); - --color-accent-yellow-800: var(--color-gamboge-800); - --color-accent-yellow-900: var(--color-gamboge-900); - --color-accent-yellow-950: var(--color-gamboge-950); - - --color-gray-100: var(--color-ebony-clay-100); - --color-gray-200: var(--color-ebony-clay-200); - --color-gray-300: var(--color-ebony-clay-300); - --color-gray-400: var(--color-ebony-clay-400); - --color-gray-500: var(--color-ebony-clay-500); - --color-gray-600: var(--color-ebony-clay-600); - --color-gray-700: var(--color-ebony-clay-700); - --color-gray-800: var(--color-ebony-clay-800); - --color-gray-900: var(--color-ebony-clay-900); - - --color-danger: var(--color-alizarin-crimson-500); - --color-danger-50: var(--color-alizarin-crimson-50); - --color-danger-100: var(--color-alizarin-crimson-100); - --color-danger-200: var(--color-alizarin-crimson-200); - --color-danger-300: var(--color-alizarin-crimson-300); - --color-danger-400: var(--color-alizarin-crimson-400); - --color-danger-500: var(--color-alizarin-crimson-500); - --color-danger-600: var(--color-alizarin-crimson-600); - --color-danger-700: var(--color-alizarin-crimson-700); - --color-danger-800: var(--color-alizarin-crimson-800); - --color-danger-900: var(--color-alizarin-crimson-900); - --color-danger-950: var(--color-alizarin-crimson-950); - - --color-success: var(--color-spring-green-500); - --color-success-50: var(--color-spring-green-50); - --color-success-100: var(--color-spring-green-100); - --color-success-200: var(--color-spring-green-200); - --color-success-300: var(--color-spring-green-300); - --color-success-400: var(--color-spring-green-400); - --color-success-500: var(--color-spring-green-500); - --color-success-600: var(--color-spring-green-600); - --color-success-700: var(--color-spring-green-700); - --color-success-800: var(--color-spring-green-800); - --color-success-900: var(--color-spring-green-900); - --color-success-950: var(--color-spring-green-950); - - --color-warning: var(--color-burning-orange-500); - --color-warning-50: var(--color-burning-orange-50); - --color-warning-100: var(--color-burning-orange-100); - --color-warning-200: var(--color-burning-orange-200); - --color-warning-300: var(--color-burning-orange-300); - --color-warning-400: var(--color-burning-orange-400); - --color-warning-500: var(--color-burning-orange-500); - --color-warning-600: var(--color-burning-orange-600); - --color-warning-700: var(--color-burning-orange-700); - --color-warning-800: var(--color-burning-orange-800); - --color-warning-900: var(--color-burning-orange-900); - --color-warning-950: var(--color-burning-orange-950); -} diff --git a/frontend-old/app/styles/theme/colors/text.css b/frontend-old/app/styles/theme/colors/text.css deleted file mode 100644 index ca38334..0000000 --- a/frontend-old/app/styles/theme/colors/text.css +++ /dev/null @@ -1,216 +0,0 @@ -.text-primary { - color: var(--color-primary); -} -.text-primary-100 { - color: var(--color-primary-100); -} -.text-primary-200 { - color: var(--color-primary-200); -} -.text-primary-300 { - color: var(--color-primary-300); -} -.text-primary-400 { - color: var(--color-primary-400); -} -.text-primary-500 { - color: var(--color-primary-500); -} -.text-primary-600 { - color: var(--color-primary-600); -} -.text-primary-700 { - color: var(--color-primary-700); -} -.text-primary-800 { - color: var(--color-primary-800); -} -.text-primary-900 { - color: var(--color-primary-900); -} - -.text-secondary { - color: var(--color-secondary) !important; -} -.text-secondary-100 { - color: var(--color-secondary-100); -} -.text-secondary-200 { - color: var(--color-secondary-200); -} -.text-secondary-300 { - color: var(--color-secondary-300); -} -.text-secondary-400 { - color: var(--color-secondary-400); -} -.text-secondary-500 { - color: var(--color-secondary-500); -} -.text-secondary-600 { - color: var(--color-secondary-600); -} -.text-secondary-700 { - color: var(--color-secondary-700); -} -.text-secondary-800 { - color: var(--color-secondary-800); -} -.text-secondary-900 { - color: var(--color-secondary-900); -} - -.text-accent-blue { - color: var(--color-accent-blue); -} -.text-accent-blue-100 { - color: var(--color-accent-blue-100); -} -.text-accent-blue-200 { - color: var(--color-accent-blue-200); -} -.text-accent-blue-300 { - color: var(--color-accent-blue-300); -} -.text-accent-blue-400 { - color: var(--color-accent-blue-400); -} -.text-accent-blue-500 { - color: var(--color-accent-blue-500); -} -.text-accent-blue-600 { - color: var(--color-accent-blue-600); -} -.text-accent-blue-700 { - color: var(--color-accent-blue-700); -} -.text-accent-blue-800 { - color: var(--color-accent-blue-800); -} -.text-accent-blue-900 { - color: var(--color-accent-blue-900); -} - -.text-accent-yellow { - color: var(--color-accent-yellow); -} -.text-accent-yellow-100 { - color: var(--color-accent-yellow-100); -} -.text-accent-yellow-200 { - color: var(--color-accent-yellow-200); -} -.text-accent-yellow-300 { - color: var(--color-accent-yellow-300); -} -.text-accent-yellow-400 { - color: var(--color-accent-yellow-400); -} -.text-accent-yellow-500 { - color: var(--color-accent-yellow-500); -} -.text-accent-yellow-600 { - color: var(--color-accent-yellow-600); -} -.text-accent-yellow-700 { - color: var(--color-accent-yellow-700); -} -.text-accent-yellow-800 { - color: var(--color-accent-yellow-800); -} -.text-accent-yellow-900 { - color: var(--color-accent-yellow-900); -} - -.text-danger { - color: var(--color-danger); -} -.text-danger-100 { - color: var(--color-danger-100); -} -.text-danger-200 { - color: var(--color-danger-200); -} -.text-danger-300 { - color: var(--color-danger-300); -} -.text-danger-400 { - color: var(--color-danger-400); -} -.text-danger-500 { - color: var(--color-danger-500); -} -.text-danger-600 { - color: var(--color-danger-600); -} -.text-danger-700 { - color: var(--color-danger-700); -} -.text-danger-800 { - color: var(--color-danger-800); -} -.text-danger-900 { - color: var(--color-danger-900); -} - -.text-warning { - color: var(--color-warning); -} -.text-warning-100 { - color: var(--color-warning-100); -} -.text-warning-200 { - color: var(--color-warning-200); -} -.text-warning-300 { - color: var(--color-warning-300); -} -.text-warning-400 { - color: var(--color-warning-400); -} -.text-warning-500 { - color: var(--color-warning-500); -} -.text-warning-600 { - color: var(--color-warning-600); -} -.text-warning-700 { - color: var(--color-warning-700); -} -.text-warning-800 { - color: var(--color-warning-800); -} -.text-warning-900 { - color: var(--color-warning-900); -} - -.text-success { - color: var(--color-success); -} -.text-success-100 { - color: var(--color-success-100); -} -.text-success-200 { - color: var(--color-success-200); -} -.text-success-300 { - color: var(--color-success-300); -} -.text-success-400 { - color: var(--color-success-400); -} -.text-success-500 { - color: var(--color-success-500); -} -.text-success-600 { - color: var(--color-success-600); -} -.text-success-700 { - color: var(--color-success-700); -} -.text-success-800 { - color: var(--color-success-800); -} -.text-success-900 { - color: var(--color-success-900); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/fonts.css b/frontend-old/app/styles/theme/fonts.css deleted file mode 100644 index eed2248..0000000 --- a/frontend-old/app/styles/theme/fonts.css +++ /dev/null @@ -1,93 +0,0 @@ - -/* Global font settings */ - -/* Set Space Grotesk as the default font */ -body { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; - font-size: 14px; - line-height: 1.6; - color: #333; -} - -/* Use Anta for headings */ -h1, h2, h3 { - font-family: 'Syncopate', sans-serif; - color: #111; -} - -/* Use Space Grotesk for smaller text like paragraphs */ -p { - font-family: 'Space Grotesk', sans-serif; -} - - -.font-default { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; -} - -.font-syncopate { - font-family: "Syncopate", serif !important; -} - -.font-space-grotesk { - font-family: 'Space Grotesk', sans-serif; -} - -.font-weight-100 { - font-weight: 100; -} -.font-weight-200 { - font-weight: 200; -} -.font-weight-300 { - font-weight: 300; -} -.font-weight-400 { - font-weight: 400; -} -.font-weight-500 { - font-weight: 500; -} -.font-weight-600 { - font-weight: 600; -} -.font-weight-700 { - font-weight: 700; -} -.font-weight-800 { - font-weight: 800; -} -.font-weight-900 { - font-weight: 900; -} - -.font-size-12 { - font-size: 12px !important; -} - -.font-size-14 { - font-size: 14px !important; -} - -.font-size-16 { - font-size: 16px !important; -} - -.font-size-18 { - font-size: 18px !important; -} - -.font-size-20 { - font-size: 20px !important; -} - -.font-size-24 { - font-size: 24px !important; -} - -.font-size-32 { - font-size: 32px !important; -} -.font-size-48 { - font-size: 48px !important; -} \ No newline at end of file diff --git a/frontend-old/app/types/DishType.ts b/frontend-old/app/types/DishType.ts deleted file mode 100644 index 99bd7f2..0000000 --- a/frontend-old/app/types/DishType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { UserType } from "@/types/UserType"; - -export type DishType = { - id: number - name: string, - recurrence: number, - users: UserType[], -} - -export type DishDateType = { - id: number; - date: string; - dish: DishType; - user: UserType; -} - -export type ScheduledDishesType = { - date: string; - dishes: { dish: DishType, user: UserType }[]; -} diff --git a/frontend-old/app/types/RecurrenceType.ts b/frontend-old/app/types/RecurrenceType.ts deleted file mode 100644 index 554c777..0000000 --- a/frontend-old/app/types/RecurrenceType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type RecurrenceType = { - type: "App\\Models\\WeeklyRecurrence" | "App\\Models\\MinimumRecurrence"; - value: number; -} diff --git a/frontend-old/app/types/ScheduleType.ts b/frontend-old/app/types/ScheduleType.ts deleted file mode 100644 index e9494af..0000000 --- a/frontend-old/app/types/ScheduleType.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import type { UserType } from "@/types/UserType"; - -export type ScheduleType = { - id: number; - date: string; - scheduled_user_dishes: ScheduledUserDishType[]; - is_skipped: boolean; -} - -export type FilledScheduleType = { - id?: number; - date: string; - is_skipped?: boolean; - scheduled_user_dishes: ScheduledUserDishType[]; -} - -export type ScheduleDataType = { - user: UserType; - scheduled_user_dish: UserDishType | null; - user_dishes: UserDishType[]; -} \ No newline at end of file diff --git a/frontend-old/app/types/ScheduledUserDishType.ts b/frontend-old/app/types/ScheduledUserDishType.ts deleted file mode 100644 index a853f3c..0000000 --- a/frontend-old/app/types/ScheduledUserDishType.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { UserType } from "@/types/UserType"; -import type { DishType } from "@/types/DishType"; -import type { RecurrenceType } from "@/types/RecurrenceType"; - -export type UserDishType = { - id: number; - dish: DishType; - user: UserType; - recurrences: RecurrenceType[]; -} - -export type ScheduledUserDishType = { - id: number; - user_dish: UserDishType; -} \ No newline at end of file diff --git a/frontend-old/app/types/UserDishType.ts b/frontend-old/app/types/UserDishType.ts deleted file mode 100644 index c4bd896..0000000 --- a/frontend-old/app/types/UserDishType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {RecurrenceType} from "@/types/RecurrenceType"; - -export type DishType = { - user: UserType; - dish: DishType; - recurrences: RecurrenceType[]; -} \ No newline at end of file diff --git a/frontend-old/app/types/UserDishWithoutUserType.ts b/frontend-old/app/types/UserDishWithoutUserType.ts deleted file mode 100644 index d41a7d9..0000000 --- a/frontend-old/app/types/UserDishWithoutUserType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { DishType } from "@/types/DishType"; -import type { RecurrenceType } from "@/types/RecurrenceType"; - -export type UserDishWithoutUserType = { - id: number; - dish: DishType; - recurrences: RecurrenceType[]; -} diff --git a/frontend-old/app/types/UserType.ts b/frontend-old/app/types/UserType.ts deleted file mode 100644 index 334111a..0000000 --- a/frontend-old/app/types/UserType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { UserDishWithoutUserType } from "@/types/UserDishWithoutUserType"; - -export type UserType = { - id: number; - name: string; - user_dishes: UserDishWithoutUserType[]; -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/apiRequest.ts b/frontend-old/app/utils/api/apiRequest.ts deleted file mode 100644 index 01acc70..0000000 --- a/frontend-old/app/utils/api/apiRequest.ts +++ /dev/null @@ -1,107 +0,0 @@ -export const apiRequest = async (url: string, options: RequestInit = {}) => { - const token = localStorage.getItem('token'); - - const allowedRequests = [ - '/api/auth/login', - '/api/auth/register', - ] - - if (allowedRequests.includes(url)) { - return publicRequest(url, options) - } - - if (!token) { - throw new Error('No authentication token found.' + url); - } - - return privateRequest(url, token, options); -}; - -export const publicRequest = async (url: string, options: RequestInit = {}) => { - console.log('→ Sending request', url, options.method); - - url = 'http://localhost' + url; - - const response = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - ...options, - }); - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - -export const privateRequest = async (fullUrl: string, token: string, options: RequestInit = {}) => { - const headers = { - ...(options.headers || {}), - Authorization: `Bearer ${token}`, - }; - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { headers, ...options }); - - // Authentication failure - token invalid - redirect to login - if (response.status === 401) { - localStorage.removeItem('token'); - localStorage.removeItem('refreshToken'); - - window.location.href = '/login'; - - throw new Error('Unauthorized. Redirecting to login.'); - } - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - - -// Add shorthand HTTP methods -apiRequest.get = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'GET' }); -}; - -apiRequest.post = | undefined>( - url: string, - body: TBody, - options: RequestInit = {}, -) => { - return apiRequest(url, { - ...options, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.put = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.delete = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'DELETE' }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/auth.ts b/frontend-old/app/utils/api/auth.ts deleted file mode 100644 index f4511aa..0000000 --- a/frontend-old/app/utils/api/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { apiRequest } from '@/utils/api/apiRequest'; - -export const login = async (email: string, password: string) => { - const data = await apiRequest.post('/api/auth/login', { email, password }); - - if (!data.access_token) { - throw new Error('No access token returned from login.'); - } - - localStorage.setItem('token', data.access_token); - - return data; -}; - - -export const register = async (name: string, email: string, password: string, passwordConfirmation: string) => { - const data = await apiRequest.post('/api/auth/register', { - name, - email, - password, - password_confirmation: passwordConfirmation, // Match the backend's expected parameter - }); - - // Store the token (if returned by the backend) similarly to login - localStorage.setItem('token', data.access_token); - - return data; -}; diff --git a/frontend-old/app/utils/api/dishApi.ts b/frontend-old/app/utils/api/dishApi.ts deleted file mode 100644 index 8d38dba..0000000 --- a/frontend-old/app/utils/api/dishApi.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {apiRequest} from "@/utils/api/apiRequest"; - -export const listDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dishes) { - return data.payload.dishes as DishType[]; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const fetchDish = async (id: number): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dish) { - return data.payload.dish as DishType; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const createDish = async ( - name: string, - // recurrence: number, - // userIds: number[] -) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes`, { - name, - // recurrence, - // users: userIds, - }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch(() => { - throw new Error("Failed to create dish. Please try again later."); - }); -}; - -export const updateDish = async (dish_id: number, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.put(`/api/dishes/${dish_id}`, {name}, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .catch((error) => { - throw error; - }); -}; - -export const deleteDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.delete(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const addUserToDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/add`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const removeUserFromDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/remove`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/app/utils/api/scheduleApi.ts b/frontend-old/app/utils/api/scheduleApi.ts deleted file mode 100644 index d7fcbf3..0000000 --- a/frontend-old/app/utils/api/scheduleApi.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { isValidDate } from "@/helpers/Date"; - -export const listSchedule = async (startDate?: string, endDate?: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (startDate && !isValidDate(startDate)) { - throw new Error('Invalid start date'); - } - if (endDate && !isValidDate(endDate)) { - throw new Error('Invalid end date'); - } - - const params = new URLSearchParams(); - if (startDate) params.append('start', startDate); - if (endDate) params.append('end', endDate); - - const endpoint = `/api/schedule${params.toString() ? `?${params.toString()}` : ''}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule); -}; - -export const getScheduleForDate = async (date: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Update the schedule for a specific date (e.g., mark as skipped) -export const updateScheduleForDate = async (date: string, isSkipped: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.put(endpoint, { is_skipped: isSkipped }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Generate a new schedule (optional: overwrite the existing one) -export const generateSchedule = async (overwrite: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const endpoint = `/api/schedule/generate`; - - return apiRequest.post(endpoint, { overwrite }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const scheduleUserDish = async (date: string, user_id: number, user_dish_id: number|null, skipped: boolean = false) => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - const endpoint = `/api/schedule/${date}/user-dishes`; - - return apiRequest.post(endpoint, { user_dish_id, user_id, skipped }, { headers: { Authorization: `Bearer ${token}`} }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { throw err }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/scheduledUserDishesApi.ts b/frontend-old/app/utils/api/scheduledUserDishesApi.ts deleted file mode 100644 index 8a9b66c..0000000 --- a/frontend-old/app/utils/api/scheduledUserDishesApi.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; - -export const listScheduledUserDishesStartingFromDate = async (startDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?start=${startDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const listScheduledUserDishesEndingAtDate = async (endDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?end=${endDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const getScheduledUserDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; - -export const updateScheduledUserDish = async (id: number, userDishId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const payload = userDishId > 0 - ? { user_dish_id: userDishId } - : { user_dish_id: null, is_skipped: true }; - - return apiRequest.put(`/api/scheduled-user-dishes/${id}`, payload, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/userDishApi.ts b/frontend-old/app/utils/api/userDishApi.ts deleted file mode 100644 index 39ba27f..0000000 --- a/frontend-old/app/utils/api/userDishApi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { UserDishType } from "@/types/ScheduledUserDishType"; - -export const listUserDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - return apiRequest.get(`/api/user-dishes`, { headers: { Authorization: `Bearer ${ token }` } }) - .then((data) => { - if (data?.payload?.user_dishes) return data.payload.user_dishes as UserDishType[]; - - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/app/utils/api/usersApi.ts b/frontend-old/app/utils/api/usersApi.ts deleted file mode 100644 index d07a9c6..0000000 --- a/frontend-old/app/utils/api/usersApi.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {RecurrenceType} from "@/types/RecurrenceType"; -import {apiRequest} from "@/utils/api/apiRequest"; -import {UserType} from "@/types/UserType"; - -export const listUsers = async () => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.users) { - return data.payload.users; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const showUser = async (userId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users/${userId}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - if (data?.payload?.user) { - return data.payload.user; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const createUser = async (name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.post('/api/users', {name}, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const updateUser = async (user: UserType, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.put(`/api/users/${user.id}`, { name }, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const deleteUser = async (user: UserType) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.delete(`/api/users/${user.id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - - -export const getUserDishForUserAndDish = async (userId: number, dishId: number) => { - const endpoint = `/api/users/${userId}/dishes/${dishId}`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.user_dish) { - return data.payload.user_dish; - } - throw new Error('Failed to fetch user dish'); - }) - .catch((err) => { - throw err; - }); -}; - -export const syncUserDishRecurrences = async ( - dish_id: number, - user_id: number, - recurrenceData: RecurrenceType[] -) => { - const url = `/api/users/${user_id}/dishes/${dish_id}/recurrences`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(url, { recurrences: recurrenceData }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch((err) => { - throw err; - }); -}; - diff --git a/frontend-old/app/utils/dateBuilder.ts b/frontend-old/app/utils/dateBuilder.ts deleted file mode 100644 index e41ce15..0000000 --- a/frontend-old/app/utils/dateBuilder.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DateTime } from "luxon"; - -const transformDate = (inputDate: string, addSuffix = false): string => { - const date = DateTime.fromISO(inputDate) - const day = date.day - const suffix = addSuffix ? getDaySuffix(day) : '' - return date.toFormat("MMMM") + ` ${ day }${ suffix }, ` + date.toFormat("yyyy"); -} - -const getDaySuffix = (day: number): string => { - if (day >= 11 && day <= 13) return "th"; - switch (day % 10) { - case 1: - return "st"; - case 2: - return "nd"; - case 3: - return "rd"; - default: - return "th"; - } -}; - -export default transformDate; \ No newline at end of file diff --git a/frontend-old/app/utils/scheduleBuilder.ts b/frontend-old/app/utils/scheduleBuilder.ts deleted file mode 100644 index 8bbf15f..0000000 --- a/frontend-old/app/utils/scheduleBuilder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ScheduleDataType, ScheduleType } from "@/types/ScheduleType"; -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -const ScheduleBuilder = ( - schedule: ScheduleType, - users: UserType[], - userDishes: UserDishType[] -): ScheduleDataType[] => users.map(user => { - return { - user, - scheduled_user_dish: schedule.scheduled_user_dishes - .filter((scheduledUserDish: ScheduledUserDishType) => scheduledUserDish.user_dish?.user.id === user.id) - .shift()?.user_dish ?? null, - user_dishes: userDishes.filter((userDish: UserDishType) => userDish.user.id === user.id) - } -}) - -export default ScheduleBuilder \ No newline at end of file diff --git a/frontend-old/app/welcome/logo-dark.svg b/frontend-old/app/welcome/logo-dark.svg deleted file mode 100644 index dd82028..0000000 --- a/frontend-old/app/welcome/logo-dark.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend-old/app/welcome/logo-light.svg b/frontend-old/app/welcome/logo-light.svg deleted file mode 100644 index 7328492..0000000 --- a/frontend-old/app/welcome/logo-light.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend-old/app/welcome/welcome.tsx b/frontend-old/app/welcome/welcome.tsx deleted file mode 100644 index 8ac6e1d..0000000 --- a/frontend-old/app/welcome/welcome.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import logoDark from "./logo-dark.svg"; -import logoLight from "./logo-light.svg"; - -export function Welcome() { - return ( -
-
-
-
- React Router - React Router -
-
-
- -
-
-
- ); -} - -const resources = [ - { - href: "https://reactrouter.com/docs", - text: "React Router Docs", - icon: ( - - - - ), - }, - { - href: "https://rmx.as/discord", - text: "Join Discord", - icon: ( - - - - ), - }, -]; diff --git a/frontend-old/archive/.dockerignore b/frontend-old/archive/.dockerignore deleted file mode 100644 index 11ee758..0000000 --- a/frontend-old/archive/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -.env.local diff --git a/frontend-old/archive/.env.local.example b/frontend-old/archive/.env.local.example deleted file mode 100644 index 87516ef..0000000 --- a/frontend-old/archive/.env.local.example +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost -#NODE_ENV=production \ No newline at end of file diff --git a/frontend-old/archive/.gitignore b/frontend-old/archive/.gitignore deleted file mode 100644 index b601ed9..0000000 --- a/frontend-old/archive/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -/.idea -/.env.local -/package-lock.json - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/frontend-old/archive/README.md b/frontend-old/archive/README.md deleted file mode 100644 index da15af8..0000000 --- a/frontend-old/archive/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# DishPlanner Front End - diff --git a/frontend-old/archive/bin/update.sh b/frontend-old/archive/bin/update.sh deleted file mode 100755 index 7eec906..0000000 --- a/frontend-old/archive/bin/update.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -echo "🔄 Pulling latest changes..." -git pull origin main - -echo "🔨 Installing dependencies..." -npm install - -echo "🏗️ Building frontend..." -npm run build - -echo "🔁 Restarting frontend service..." -sudo systemctl restart dishplanner-frontend - -echo "✅ Update complete!" diff --git a/frontend-old/archive/build_and_push.sh b/frontend-old/archive/build_and_push.sh deleted file mode 100755 index ba9f2c6..0000000 --- a/frontend-old/archive/build_and_push.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -docker build -t 192.168.178.152:50114/dishplanner-frontend:latest . -docker push 192.168.178.152:50114/dishplanner-frontend:latest \ No newline at end of file diff --git a/frontend-old/archive/eslint.config.mjs b/frontend-old/archive/eslint.config.mjs deleted file mode 100644 index c85fb67..0000000 --- a/frontend-old/archive/eslint.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; - -export default eslintConfig; diff --git a/frontend-old/archive/next.config.ts b/frontend-old/archive/next.config.ts deleted file mode 100644 index 536bef3..0000000 --- a/frontend-old/archive/next.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - // Rewrite /api/* to backend container (from client-side) - async rewrites() { - return [ - { - source: "/api/:path*", - destination: "http://backend:80/api/:path*", // internal Docker DNS - }, - ]; - }, -}; - -export default nextConfig; diff --git a/frontend-old/archive/package.json b/frontend-old/archive/package.json deleted file mode 100644 index 62e44db..0000000 --- a/frontend-old/archive/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "dish-planner", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "next lint", - "export": "next export" - }, - "dependencies": { - "@headlessui/react": "^2.2.0", - "@heroicons/react": "^2.2.0", - "classnames": "^2.5.1", - "luxon": "^3.5.0", - "next": "15.2.4", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@types/luxon": "^3.4.2", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "15.1.5", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" - } -} diff --git a/frontend-old/archive/postcss.config.mjs b/frontend-old/archive/postcss.config.mjs deleted file mode 100644 index 1a69fd2..0000000 --- a/frontend-old/archive/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/frontend-old/archive/public/dish-planner.webp b/frontend-old/archive/public/dish-planner.webp deleted file mode 100644 index 3eaa04f..0000000 Binary files a/frontend-old/archive/public/dish-planner.webp and /dev/null differ diff --git a/frontend-old/archive/public/file.svg b/frontend-old/archive/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/frontend-old/archive/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/globe.svg b/frontend-old/archive/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/frontend-old/archive/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/next.svg b/frontend-old/archive/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/frontend-old/archive/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/vercel.svg b/frontend-old/archive/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/frontend-old/archive/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/window.svg b/frontend-old/archive/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/frontend-old/archive/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx b/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx deleted file mode 100644 index 95627e2..0000000 --- a/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import {useEffect, useState} from "react"; -import {use} from "react"; -import PageTitle from "@/components/ui/PageTitle"; -import {DishType} from "@/types/DishType"; -import {useRouter} from "next/navigation"; -import Link from "next/link"; -import Alert from "@/components/ui/Alert"; -import useRoutes from "@/hooks/useRoutes"; -import {deleteDish, fetchDish} from "@/utils/api/dishApi"; -import {UserType} from "@/types/UserType"; - -export default function EditDishPage({params}: { params: Promise<{ id: number }> }) { - const [name, setName] = useState(""); - const [recurrence, setRecurrence] = useState(0); - const [users, setUsers] = useState([]); - const [error, setError] = useState(""); - const [isLoading, setIsLoading] = useState(true); // To handle loading state - const {id} = use(params) - const router = useRouter() - const routes = useRoutes(); - - useEffect(() => { - fetchDish(id) - .then((dish: DishType) => { - setName(dish.name); - setRecurrence(dish.recurrence); - setUsers(dish.users); - }) - .catch((err) => setError(err)) - .finally(() => setIsLoading(false)); - }, [id]); // Only run when `params.id` changes - - const submitForm = (e: React.MouseEvent) => { - e.preventDefault() - - deleteDish(id) - .then(() => router.push(routes.dish.index())) - .catch((err) => setError(err)) - } - - if (isLoading) { - return

Loading...

; - } - - return ( -
-
- Delete Dish -
- - { - error != '' && { error } - } - -
-
- Are you sure you want to delete this dish? -
-
- name: {name}
- recurrence: {recurrence} - users: {users.map((user) => user.name).join(', ')} -
- - -
- No, take me back -
- - -
-
- ); -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx b/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx deleted file mode 100644 index 364f9b1..0000000 --- a/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx +++ /dev/null @@ -1,59 +0,0 @@ -"use client"; - -import {use, useCallback, useEffect, useState} from "react"; -import PageTitle from "@/components/ui/PageTitle"; -import EditDishForm from "@/components/features/dishes/EditDishForm"; -import {DishType} from "@/types/DishType"; -import Spinner from "@/components/Spinner"; -import {fetchDish} from "@/utils/api/dishApi"; -import SyncUsersForm from "@/components/features/dishes/SyncUsersForm"; -import {ChevronLeftIcon} from "@heroicons/react/16/solid"; -import useRoutes from "@/hooks/useRoutes"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; -import Hr from "@/components/ui/Hr" - -export default function EditDishPage({ params }: { params: Promise<{ id: number }> }) { - const { id } = use(params) - const [dish, setDish] = useState(null) - const [isLoading, setIsLoading] = useState(true); - const routes = useRoutes(); - - const loadDish = useCallback(async () => { - try { - const fetchedDish = await fetchDish(id); - setDish(fetchedDish); - } catch (error) { - console.error("Error fetching dish:", error); - throw new Error('No token found in localStorage.'); - } finally { - setIsLoading(false); - } - }, [id]); - - useEffect(() => { - loadDish(); - }, [loadDish]); - - - if (isLoading || dish === null) { - return - } - - return ( -
-
- Edit Dish - - -

BACK

-
-
- - - -
- - -
- ); -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/create/page.tsx b/frontend-old/archive/src/app/dishes/create/page.tsx deleted file mode 100644 index 156c038..0000000 --- a/frontend-old/archive/src/app/dishes/create/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -"use client"; - -import CreateDishForm from "@/components/features/dishes/CreateDishForm"; - -export default function CreateDishPage() { - return -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/page.tsx b/frontend-old/archive/src/app/dishes/page.tsx deleted file mode 100644 index 274a848..0000000 --- a/frontend-old/archive/src/app/dishes/page.tsx +++ /dev/null @@ -1,57 +0,0 @@ -"use client"; - -import PageTitle from "@/components/ui/PageTitle"; -import {DishType} from "@/types/DishType"; -import Dish from "@/components/features/dishes/Dish"; -import {PlusIcon} from "@heroicons/react/24/solid"; -import {useEffect, useState} from "react"; -import useRoutes from "@/hooks/useRoutes"; -import {listDishes} from "@/utils/api/dishApi"; -import Button from "@/components/ui/Button" -export const dynamic = 'force-dynamic'; - -export default function DishesIndexPage() { - const routes = useRoutes(); - - const [dishes, setDishes] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .finally(() => setLoading(false)); - }, []); - - if (loading) return

Loading...

; - - if (! dishes) { - return

Loading...

- } - - return ( - <> -
-
- Dishes -
-
- -
-
- - { - dishes.length === 0 - ?

No dishes found :(

- : dishes.map((dish: DishType, index: number) => ) - } - - ); -} diff --git a/frontend-old/archive/src/app/favicon.ico b/frontend-old/archive/src/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/frontend-old/archive/src/app/favicon.ico and /dev/null differ diff --git a/frontend-old/archive/src/app/layout.tsx b/frontend-old/archive/src/app/layout.tsx deleted file mode 100644 index f6d242b..0000000 --- a/frontend-old/archive/src/app/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react"; -import type { Metadata } from 'next'; -import NavBar from '@/components/layout/NavBar'; -import { AuthProvider } from '@/context/AuthContext'; -import AuthGuard from "@/components/layout/AuthGuard"; -import '@/styles/main.css'; - -export const metadata: Metadata = { - title: 'DishPlanner', - description: 'Schedule your dishes', -}; - -export default function RootLayout({children,}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - Dish Planner - - - - - -
{children}
-
-
- - - ); -} diff --git a/frontend-old/archive/src/app/login/page.tsx b/frontend-old/archive/src/app/login/page.tsx deleted file mode 100644 index cf92a57..0000000 --- a/frontend-old/archive/src/app/login/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; - -import LoginForm from "@/components/features/auth/LoginForm"; - -export default function LoginPage() { - return ( -
- -
- ); -} diff --git a/frontend-old/archive/src/app/page.tsx b/frontend-old/archive/src/app/page.tsx deleted file mode 100644 index 8c98217..0000000 --- a/frontend-old/archive/src/app/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import UpcomingDishes from "@/components/features/schedule/UpcomingDishes"; -export const dynamic = 'force-dynamic'; - -export default async function FrontPage() { - - return ( - - ); -} diff --git a/frontend-old/archive/src/app/register/page.tsx b/frontend-old/archive/src/app/register/page.tsx deleted file mode 100644 index 1cad575..0000000 --- a/frontend-old/archive/src/app/register/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -'use client'; - -import RegistrationForm from "@/components/features/auth/RegistrationForm"; - -const RegistrationPage = () => { - return ( -
- -
- ); - -} - -export default RegistrationPage \ No newline at end of file diff --git a/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx b/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx deleted file mode 100644 index a1d3097..0000000 --- a/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use client"; - -import { use } from "react"; -import ScheduleEditForm from "@/components/features/schedule/ScheduleEditForm"; - -const ScheduleEditPage = ({ params }: { params: Promise<{ date: string }> }) => { - const { date } = use(params) - - return -} - -export default ScheduleEditPage \ No newline at end of file diff --git a/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx b/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx deleted file mode 100644 index aa6f772..0000000 --- a/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import HistoricalDishes from "@/components/features/schedule/HistoricalDishes"; -export const dynamic = 'force-dynamic'; - -export default async function HistoryPage() { - - return ( - - ); -} diff --git a/frontend-old/archive/src/app/users/[id]/edit/page.tsx b/frontend-old/archive/src/app/users/[id]/edit/page.tsx deleted file mode 100644 index 3545ae0..0000000 --- a/frontend-old/archive/src/app/users/[id]/edit/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client' - -import {FC, use, useEffect, useState} from "react"; -import {UserType} from "@/types/UserType"; -import {showUser} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import EditUserForm from "@/components/features/users/EditUserForm"; -export const dynamic = 'force-dynamic'; - -interface Props { - params: Promise<{ id: number }>; -} - -const UpdateUsersPage: FC = ({ params }) => { - const { id } = use(params) - const [user, setUser] = useState(null) - - useEffect(() => { - showUser(id) - .then((user: UserType) => setUser(user)) - }, [id]); - - if (!user) { - return - } - - return -} - -export default UpdateUsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/app/users/create/page.tsx b/frontend-old/archive/src/app/users/create/page.tsx deleted file mode 100644 index 80480f9..0000000 --- a/frontend-old/archive/src/app/users/create/page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -'use client' - -import PageTitle from "@/components/ui/PageTitle"; -import useRoutes from "@/hooks/useRoutes"; -import {useRouter} from "next/navigation"; -import {useState} from "react"; -import Alert from "@/components/ui/Alert"; -import {createUser} from "@/utils/api/usersApi"; -import Link from "next/link"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -export const dynamic = 'force-dynamic'; - -const CreateUsersPage = () => { - const [name, setName] = useState(''); - const [error, setError] = useState(''); - const router = useRouter(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - createUser(name) - .then(() => { - router.push(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Create -
-
- ); -} - -export default CreateUsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/app/users/page.tsx b/frontend-old/archive/src/app/users/page.tsx deleted file mode 100644 index 0943dd8..0000000 --- a/frontend-old/archive/src/app/users/page.tsx +++ /dev/null @@ -1,78 +0,0 @@ -'use client' - -import PageTitle from "@/components/ui/PageTitle"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import useRoutes from "@/hooks/useRoutes"; -import Link from "next/link"; -import {PencilIcon, PlusIcon, TrashIcon} from "@heroicons/react/24/solid"; -import React from "react"; -import {deleteUser} from "@/utils/api/usersApi"; -import {UserType} from "@/types/UserType"; -import Card from "@/components/layout/Card"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; - -const UsersPage = () => { - const { users, isLoading } = useFetchUsers(); - const routes = useRoutes(); - - const handleDelete = (user: UserType) => { - deleteUser(user) - .then(() => window.location.reload()) - } - - if (isLoading) { - return ; - } - - const usersList = () => { - return users.map((user) => ( - -
- {user.name} -
-
-
- -
- -
- -
-
- handleDelete(user)}> -
- -
- -
-
-
- )) - }; - - return ( -
-
-
- Users -
- -
- - -

Add User

-
-
-
- - { - users && users.length > 0 - ? usersList() - :
No users
- } -
- ); -} - -export default UsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/components/Spinner.tsx b/frontend-old/archive/src/components/Spinner.tsx deleted file mode 100644 index f170616..0000000 --- a/frontend-old/archive/src/components/Spinner.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const Spinner = () => { - - return ( -
- - - - -
- ) - -} - -export default Spinner \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/OnboardingBanner.tsx b/frontend-old/archive/src/components/features/OnboardingBanner.tsx deleted file mode 100644 index 9bbc003..0000000 --- a/frontend-old/archive/src/components/features/OnboardingBanner.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { FC } from "react" -import Link from "next/link" -import useRoutes from "@/hooks/useRoutes" -import { UserType } from "@/types/UserType" -import { DishType } from "@/types/DishType" - -interface Props { - dishes: DishType[], - users: UserType[] -} - -const OnboardingBanner: FC = ({ dishes, users }) => { - const routes = useRoutes(); - - const steps = [ - { - label: "Create a user", - href: routes.user.create(), - count: users.length - }, { - label: "Create a dish", - href: routes.dish.create(), - count: dishes.length - } - ] - - return ( -
-
Welcome to DishPlanner
-
To get you started, please follow these steps to set up your account. This will ensure a better - experience. -
- - { - steps.map((step, index) => ( -
- { - step.count === 0 - ? { step.label } - :
{ step.label }
- } -
- )) - } -
- ) -} - -export default OnboardingBanner; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/auth/LoginForm.tsx b/frontend-old/archive/src/components/features/auth/LoginForm.tsx deleted file mode 100644 index 2189c36..0000000 --- a/frontend-old/archive/src/components/features/auth/LoginForm.tsx +++ /dev/null @@ -1,96 +0,0 @@ -'use client'; - -import React, { useEffect, useState } from 'react'; -import { useAuth } from '@/context/AuthContext'; -import { login } from "@/utils/api/auth"; -import { useRouter } from 'next/navigation'; -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import { useSearchParams } from 'next/navigation'; -import Alert from "@/components/ui/Alert"; - -export default function LoginForm() { - const { login: authLogin } = useAuth(); - const router = useRouter(); - const routes = useRoutes(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const searchParams = useSearchParams(); - const [alertSuccess, setAlertSuccess] = useState([]) - - // handle registration success message - const isRegistered = searchParams.get('registered') === 'true'; - - useEffect(() => { - if (isRegistered) { - setAlertSuccess(['Registration successful!',' You can now log in.']); - - const timer = setTimeout(() => { - const params = new URLSearchParams(searchParams.toString()); - params.delete('registered'); - const newUrl = `${window.location.pathname}?${params.toString()}`; - - router.replace(newUrl); - }, 3000); - - return () => clearTimeout(timer) - } - }, [isRegistered, router, searchParams]) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - - try { - await login(email, password); - authLogin(); - router.replace('/'); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Login failed'; - setError(errorMessage); - } - }; - - return ( -
- { alertSuccess.length > 0 && - - {alertSuccess.map((msg, index) => ( - - {msg} -
-
- ))} -
- } -
- {error &&

{error}

} - setEmail(e.target.value)} - required - className="w-full p-2 mb-4 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value)} - required - className="w-full p-2 mb-4 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - Login - - Create an account - -
-
- ); -} diff --git a/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx b/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx deleted file mode 100644 index 0738381..0000000 --- a/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx +++ /dev/null @@ -1,104 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { register } from "@/utils/api/auth"; -import { useRouter } from 'next/navigation'; -import useRoutes from "@/hooks/useRoutes"; -import Link from "next/link"; -import SectionTitle from "@/components/ui/SectionTitle"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -export default function LoginForm() { - const router = useRouter(); - const routes = useRoutes(); - const [name, setName] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [passwordAgain, setPasswordAgain] = useState(''); - const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [isRegistered, setIsRegistered] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (password !== passwordAgain) { - setError("Passwords do not match."); - return; - } - - try { - setIsLoading(true); - - await register(name, email, password, passwordAgain); - - router.replace('/login?registered=true'); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Registration\n failed'; - setError(errorMessage); - } finally { - setIsRegistered(true); - setIsLoading(false); - } - }; - - if (isRegistered) { - return
- Registration successful! - Please continue to the login page. -
- } - - return ( -
-
-

Register

- { error &&

{ error }

} - setName(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setEmail(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPasswordAgain(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Create Account - - - Back to Login - -
-
- ); -} diff --git a/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx b/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx deleted file mode 100644 index 2e2d2c8..0000000 --- a/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { FC, useState } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import {addUserToDish} from "@/utils/api/dishApi"; -import OutlineButton from "@/components/ui/Buttons/OutlineButton"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const AddUserToDishForm: FC = ({ dish, reloadDish }) => { - const [showAdd, setShowAdd] = useState(false); - const [selectedUser, setSelectedUser] = useState("-1"); - const { users, isLoading: isUsersLoading } = useFetchUsers(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (selectedUser === "-1") { - alert("Please select a valid user."); - return; - } - - const userToAdd = users.find((user: UserType) => user.id === parseInt(selectedUser)); - - if (!userToAdd) { - alert("User not found."); - return; - } - - addUserToDish(dish.id, userToAdd.id) - .then(() => { - setShowAdd(false); - setSelectedUser("-1"); - reloadDish(); - }) - .catch(() => { - alert("Failed to add user, please try again."); - }); - }; - - if (isUsersLoading) { - return ; - } - - const remainingUsers = users.filter( - (user: UserType) => - !dish.users.find((dishUser: UserType) => dishUser.id === user.id) - ); - - return ( - <> - setShowAdd(!showAdd)} - disabled={remainingUsers.length === 0} - type="button" - > - Add User - - - { showAdd && ( -
-
-
-
- -
- -
- - Add User - -
-
-
-
- )} - - ); -}; - -export default AddUserToDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx b/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx deleted file mode 100644 index 242c1f2..0000000 --- a/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState } from "react"; -import { useRouter } from "next/navigation"; -import { createDish } from "@/utils/api/dishApi"; -import PageTitle from "@/components/ui/PageTitle"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" - -const CreateDishForm = () => { - const router = useRouter() - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - // Validate client-side input - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await createDish(name); - if (result) { - router.push('/dishes') - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred."); - } finally { - setLoading(false); - } - } - - return ( -
-
- Create Dish -
- -
- { error && ( - { error } - ) } - -
- - setName(e.target.value) } // Update the name state on change - className="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-secondary focus:bg-gray-900" - placeholder="Enter dish name" - /> -
- - - { loading ? "Saving..." : "Save Changes" } - -
- -
- - } - > - Back to dishes - -
- ); - - -}; - -export default CreateDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/Dish.tsx b/frontend-old/archive/src/components/features/dishes/Dish.tsx deleted file mode 100644 index 7933a41..0000000 --- a/frontend-old/archive/src/components/features/dishes/Dish.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {DishType} from "@/types/DishType"; - -import {PencilIcon, TrashIcon} from '@heroicons/react/24/solid' -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import {UserType} from "@/types/UserType"; -import Card from "@/components/layout/Card"; - -const Dish = ({ dish }: { dish: DishType}) => { - const routes = useRoutes(); - - return ( - -
-

{ dish.name }

- - { - dish.users.map((user: UserType) => ( -
{user.name.slice(0, 1)}
- )) - } -
-
- -
- -
- - -
- -
- -
-
- ) -} - -export default Dish \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/DishCard.tsx b/frontend-old/archive/src/components/features/dishes/DishCard.tsx deleted file mode 100644 index 66b2c52..0000000 --- a/frontend-old/archive/src/components/features/dishes/DishCard.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {FC} from "react"; -import {DishType} from "@/types/DishType"; - -interface Props { - user: UserType, - dish: DishType, -} - -const DishCard: FC = ({ user, dish }: Props) => { - return ( -
-
- { user.name.slice(0, 1) } -
-
- { dish ? dish.name : '-' } -
-
- ) -} - -export default DishCard \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx b/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx deleted file mode 100644 index cfffb2a..0000000 --- a/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, {FC, useState} from "react"; -import {useRouter} from "next/navigation"; -import Alert from "@/components/ui/Alert"; -import {updateDish} from "@/utils/api/dishApi"; -import {DishType} from "@/types/DishType"; -import useRoutes from "@/hooks/useRoutes"; -import Spinner from "@/components/Spinner"; -import Button from "@/components/ui/Button" - -interface Props { - dish: DishType -} - -const EditDishForm: FC = ({ dish }) => { - const [name, setName] = useState(dish.name); - const [error, setError] = useState(""); - const router = useRouter() - const [loading, setLoading] = useState(false); - const routes = useRoutes(); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await updateDish(dish.id, name); - if (result) { - router.push(routes.dish.index()) - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred"); - } finally { - setLoading(false); // Reset loading state - } - } - - if (loading) { - return ; - } - - return ( -
- { - error != '' && { error } - } - - {/* Dish name input */} -
- - setName(e.target.value)} // Update the name state on change - className="p-2 border rounded w-full bg-gray-500 border-secondary background-secondary" - /> -
- - {/* Save button */} - -
- ); -} - -export default EditDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx b/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx deleted file mode 100644 index eb4bc06..0000000 --- a/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, {FC} from "react"; -import SectionTitle from "@/components/ui/SectionTitle"; -import {syncUserDishRecurrences} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import {UserDishType} from "@/types/ScheduledUserDishType"; -import {RecurrenceType} from "@/types/ScheduleType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - userDish: UserDishType - onSubmit: () => void -} - -const EditDishUserCardEditForm: FC = ({ userDish, onSubmit}) => { - const weeklyRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\WeeklyRecurrence') - const minimumRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\MinimumRecurrence') - - const wv = weeklyRecurrence ? weeklyRecurrence.value : undefined - const mv = minimumRecurrence ? minimumRecurrence.value : undefined - - const [isWeeklyOn, setIsWeeklyOn] = React.useState(weeklyRecurrence !== undefined); - const [isMinimumOn, setIsMinimumOn] = React.useState(minimumRecurrence !== undefined); - const [weekday, setWeekday] = React.useState(wv ?? 0); - const [minimumValue, setMinimumValue] = React.useState(mv ?? 7); - const [loading, setLoading] = React.useState(false); - - const handleSubmit = () => { - const recurrences = [] - - if (isWeeklyOn) { - recurrences.push({ - type: 'App\\Models\\WeeklyRecurrence', - value: weekday, - }); - } - - if (isMinimumOn) { - recurrences.push({ - type: 'App\\Models\\MinimumRecurrence', - value: minimumValue, - }); - } - - setLoading(true) - syncUserDishRecurrences(userDish.dish.id, userDish.user.id, recurrences as RecurrenceType[]) - .then((data) => console.log('request data', data)) - .finally(() => { - setLoading(false) - onSubmit() - }) - } - - if (loading) { - return ; - } - - return ( -
- Recurrences - -
-
- setIsWeeklyOn(!isWeeklyOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- { - isWeeklyOn && ( -
- - -
- ) - } -
- -
-
- setIsMinimumOn(!isMinimumOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-500 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- - { - isMinimumOn && ( -
- setMinimumValue(parseInt(e.currentTarget.value))} min="0" max="365" className="background-secondary border-secondary border-2 w-12 px-2" /> - -
- ) - } -
- - Save -
- ); -} - -export default EditDishUserCardEditForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx b/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx deleted file mode 100644 index bba81ba..0000000 --- a/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import {FC} from "react"; -import {RecurrenceType} from "@/types/ScheduleType"; - -interface Props { - recurrences: RecurrenceType[]; -} - -const RecurrenceLabels: FC = ({recurrences}) => { - const weeklyRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\WeeklyRecurrence'); - const minimumRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\MinimumRecurrence'); - - const renderWeeklyRecurrence = () => { - if (weeklyRecurrences == undefined || weeklyRecurrences.length == 0) { - return ''; - } - - const weekdayString = (() => { - switch (weeklyRecurrences[0].value) { - case 0: return "Sunday" - case 1: return "Monday" - case 2: return "Tuesday"; - case 3: return "Wednesday"; - case 4: return "Thursday"; - case 5: return "Friday"; - case 6: return "Saturday"; - default: return "Invalid day"; - } - }) - - return ( -
- { weekdayString() } -
- ) - } - const renderMinimumRecurrence = () => { - if (minimumRecurrences == undefined || minimumRecurrences.length == 0) { - return ''; - } - - return ( -
- min: { minimumRecurrences[0].value } -
- ) - } - - return <> - { renderWeeklyRecurrence() } - { renderMinimumRecurrence() } - ; -}; - -export default RecurrenceLabels; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx b/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx deleted file mode 100644 index 296167d..0000000 --- a/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import UserDishCard from "@/components/features/dishes/UserDishCard"; -import SectionTitle from "@/components/ui/SectionTitle"; -import AddUserToDishForm from "@/components/features/dishes/AddUserToDishForm"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const SyncUsersForm: FC = ({ dish, reloadDish }) => { - return ( -
- Users - - - - {dish.users.map((user: UserType) => ( - - ))} -
- ); -}; - -export default SyncUsersForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx b/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx deleted file mode 100644 index 4e06e8c..0000000 --- a/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, {FC, useEffect} from "react"; -import {DishType} from "@/types/DishType"; -import {UserType} from "@/types/UserType"; -import Link from "next/link"; -import {PencilIcon, TrashIcon} from "@heroicons/react/24/solid"; -import {removeUserFromDish} from "@/utils/api/dishApi"; -import EditDishUserCardEditForm from "@/components/features/dishes/EditDishUserCardEditForm"; -import {getUserDishForUserAndDish} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import RecurrenceLabels from "@/components/features/dishes/RecurrenceLabels"; -import {UserDishType} from "@/types/ScheduledUserDishType"; - -interface Props { - dish: DishType - user: UserType - reloadDish: () => void -} - -const UserDishCard: FC = ({dish, user, reloadDish}) => { - const [userDish, setUserDish] = React.useState(null); - const [userDishLoading, setUserDishLoading] = React.useState(true); - const [isEditMode, setIsEditMode] = React.useState(false); - - useEffect(() => { - getUserDishForUserAndDish(user.id, dish.id) - .then((userDish) => setUserDish(userDish)) - .finally(() => setUserDishLoading(false)) - }, [dish, user]); - - const handleRemove = () => { - removeUserFromDish(dish.id, user.id) - .then(() => reloadDish()) - .catch(() => { - alert("Failed to remove user, please try again."); - }); - }; - - if (userDishLoading || !userDish) { - return - } - - const onUserCardSubmit = () => { - setIsEditMode(false); - reloadDish() - } - - return ( -
-
-
- {user.name} -
- -
- -
- -
- setIsEditMode(!isEditMode)} href="#"> -
- -
- -
-
- -
- -
- -
-
- - {isEditMode && ( -
- -
- )} -
- ); -} - -export default UserDishCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx b/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx deleted file mode 100644 index 3e8639a..0000000 --- a/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import Link from "next/link"; -import React, {FC} from "react"; -import useRoutes from "@/hooks/useRoutes"; -import classNames from "classnames"; - -interface Props { - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; - handleLogout: (e: React.MouseEvent) => void; -} - -const divStyles = classNames( - 'absolute', 'text-xxl', 'rounded-b', 'top-full mt-1', 'left-0', 'right-0', '', 'py-2', - 'bg-gray-600', 'border-secondary', 'shadow-md', 'flex', 'flex-col', 'space-y-3', - 'md:hidden' -) - -const linkStyles = classNames( - 'border-b-2', 'border-secondary', 'uppercase', - 'text-primary', 'hover:background-secondary', 'pb-2', 'pl-5', - 'space-grotesk', 'text-xl' -) - -const MobileDropdownMenu: FC = ({ isOpen, setIsOpen, handleLogout }) => { - const routes = useRoutes(); - - if (!isOpen) return null; - - return ( -
- setIsOpen(false)} - > - Home - - setIsOpen(false)} - > - Dishes - - setIsOpen(false)} - > - Users - - setIsOpen(false)} - > - History - - - Logout - -
- ) -} - -export default MobileDropdownMenu \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx b/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx deleted file mode 100644 index ad3718f..0000000 --- a/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client" - -import {useEffect, useState} from "react"; -import {DateTime} from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import {ScheduleType} from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import {listSchedule} from "@/utils/api/scheduleApi"; - -const HistoricalDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const yesterday = DateTime.now().minus({ days: 1 }).toFormat('yyyy-LL-dd'); - - useEffect(() => { - listSchedule(undefined, yesterday) - .then((dishes: ScheduleType[]) => dishes - .sort((a: ScheduleType, b: ScheduleType) => new Date(b.date).getTime() - new Date(a.date).getTime()) - ) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)) - }, [yesterday]); - - if (isLoading) { - return ; - } - - if (!schedule || Object.keys(schedule).length === 0) { - return ( -
- No dishes scheduled -
- ); - } - - return
- History - -
-} - -export default HistoricalDishes \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx deleted file mode 100644 index b146f6a..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx +++ /dev/null @@ -1,75 +0,0 @@ -"use client" - -import {FC} from "react"; -import ScheduleDayCard from "@/components/features/schedule/dayCard/ScheduleDayCard"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; - -const generateDates = (startDate: string, days: number): string[] => { - const dates = []; - const start = new Date(startDate); - - for (let i = 0; i < days; i++) { - const currentDate = new Date(start); - currentDate.setDate(start.getDate() + i); - dates.push(currentDate.toISOString().split('T')[0]); - } - - return dates; -}; - - -const fillCalendar = (schedules: ScheduleType[]): FilledScheduleType[] => { - /* -Array(14) - 0: - date: "2025-05-05" - id: 2 - is_skipped: false - scheduled_user_dishes: [] - */ - - const dates = generateDates((new Date()).toISOString().split('T')[0], 31) - - return dates.map((date): FilledScheduleType => { - console.log(date) - - const schedule = schedules.find((schedule: ScheduleType) => schedule.date == date) - - if (schedule) { - return schedule - } - - return { - date, - scheduled_user_dishes: [] - } - }) -} - -interface Props { - schedule: ScheduleType[]; -} - -const ScheduleCalendar: FC = ({ schedule }: Props) => { - const {users, isLoading: areUsersLoading} = useFetchUsers(); - - if (areUsersLoading) return - - const fullCalendar = fillCalendar(schedule) - - return ( -
- { fullCalendar.map((schedule, index) => ( - - ))} -
- ) -} - -export default ScheduleCalendar \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx deleted file mode 100644 index 0f436a9..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; - -import React, { FC, useEffect, useState } from "react"; -import { ScheduleType } from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import PageTitle from "@/components/ui/PageTitle"; -import { getScheduleForDate, scheduleUserDish, updateScheduleForDate } from "@/utils/api/scheduleApi"; -import { UserDishType } from "@/types/ScheduledUserDishType"; -import Label from "@/components/ui/Label"; -import SectionTitle from "@/components/ui/SectionTitle"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import { listUserDishes } from "@/utils/api/userDishApi"; -import scheduleBuilder from "@/utils/scheduleBuilder"; -import transformDate from "@/utils/dateBuilder"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" -import Button from "@/components/ui/Button" - -interface Props { - date: string; -} - -const ScheduleEditForm: FC = ({ date }) => { - const [schedule, setSchedule] = useState() - const [userDishes, setUserDishes] = useState([]) - const [isScheduleLoading, setIsScheduleLoading] = useState(true); - const [areUserDishesLoading, setAreUserDishesLoading] = useState(true); - const { users } = useFetchUsers(); - - useEffect(() => { - getScheduleForDate(date) - .then((sched: ScheduleType) => setSchedule(sched)) - .finally(() => setIsScheduleLoading(false)) - }, [date]); - - - useEffect(() => { - listUserDishes() - .then((user_dishes: UserDishType[]) => setUserDishes(user_dishes)) - .finally(() => setAreUserDishesLoading(false)) - }, []); - - const handleSkipDay = () => { - updateScheduleForDate(date, true) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleUnskipDay = () => { - updateScheduleForDate(date, false) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleChange = (e: React.ChangeEvent, userId: number) => { - const userDishId = parseInt(e.currentTarget.value); - - if (userDishId === 0) { - scheduleUserDish(date, userId, null, true).then(() => window.location.reload()); - return; - } - - scheduleUserDish(date, userId, userDishId).then(() => window.location.reload()); - } - - if (isScheduleLoading || areUserDishesLoading || !schedule) { - return - } - - const scheduleData = scheduleBuilder(schedule, users, userDishes) - - return
-
-
- Edit Day -
-
- { transformDate(schedule.date) } -
-
- -
- - { - userDishes.length === 0 &&
-
No dishes found assigned to this user.
-
Go ahead and add some first, or choose to skip the day.
-
(dishes ={`>`} edit ={`>`} add user)
-
- } - - { schedule.is_skipped - ? - : ( - <> - { - scheduleData - .map((scheduleData) =>
-
{ scheduleData.user.name }
-
- -
-
) - } - - ) - } - -
Changes are saved automatically
- -
- -
- -
{ - schedule.is_skipped - ? - : - }
-
-
-} - -export default ScheduleEditForm \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx deleted file mode 100644 index 5b60c2b..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import {FC, useState} from "react"; -import Modal from "@/components/ui/Modal"; -import ScheduleRegenerateForm from "@/components/features/schedule/ScheduleRegenerateForm"; -import {ArrowPathIcon} from "@heroicons/react/16/solid"; - -interface ScheduleRegenerateButtonProps { - onModalClose?: () => void; -} - -const ScheduleRegenerateButton: FC = ({ onModalClose }) => { - const [open, setOpen] = useState(false); - - const handleCloseModal = () => { - setOpen(false) - if (onModalClose) { - onModalClose() - } - } - - const modalChildren = handleCloseModal()}/> - const buttonChild =
-
- - return -}; - -export default ScheduleRegenerateButton; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx deleted file mode 100644 index 017b8c2..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {DialogTitle} from "@headlessui/react"; -import Toggle from "@/components/ui/Toggle"; -import {FC, useEffect, useState} from "react"; -import {generateSchedule} from "@/utils/api/scheduleApi"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface ScheduleRegenerateFormProps { - closeModal: () => void; -} - -const ScheduleRegenerateForm: FC = ({closeModal}) => { - const [overwrite, setOverwrite] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - }, [overwrite]); - - const close = () => { - closeModal(); - } - - const handleToggle = () => { - setOverwrite(!overwrite) - } - - const handleSubmit = () => { - generateSchedule(overwrite) - .then(() => close()) - .catch((err) => setError(err)) - } - - return <> -
-
-
- - Regenerate Schedule - -
-
- { - error && { error } - } -
- -
-
- -
-
-
- - -
-
-
-
- handleSubmit()} - className="inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold shadow-xs sm:ml-3 sm:w-auto" - > - Regenerate - - close()} - className="mt-3 inline-flex w-full justify-center rounded-md bg-gray-500 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs border-secondary ring-inset sm:mt-0 sm:w-auto" - > - Cancel - -
- ; -}; - -export default ScheduleRegenerateForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx b/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx deleted file mode 100644 index c7731d0..0000000 --- a/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx +++ /dev/null @@ -1,62 +0,0 @@ -"use client" - -import { useCallback, useEffect, useState } from "react"; -import { DateTime } from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import Spinner from "@/components/Spinner"; -import { ScheduleType } from "@/types/ScheduleType"; -import { listSchedule } from "@/utils/api/scheduleApi"; -import OnboardingBanner from "@/components/features/OnboardingBanner" -import { useFetchUsers } from "@/hooks/useFetchUsers" -import { useFetchDishes } from "@/hooks/useFetchDishes" -import ScheduleRegenerateButton from "@/components/features/schedule/ScheduleRegenerateButton"; - -const UpcomingDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const today = DateTime.now().toFormat("yyyy-LL-dd"); - - const fetchSchedule = useCallback(() => { - setIsLoading(true); - listSchedule(today) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)); - }, [today]); - - useEffect(() => { - fetchSchedule(); - }, [fetchSchedule]); - - const { users, isLoading: areUsersLoading } = useFetchUsers(); - const { dishes, isLoading: areDishesLoading } = useFetchDishes(); - - if (isLoading || areUsersLoading || areDishesLoading) { - return ; - } - - if (users.length === 0 || dishes.length === 0) { - return - } - - return ( -
-
-
- Schedule -
-
- -
-
- { - !schedule || Object.keys(schedule).length === 0 - ?
No dishes scheduled
- : - } -
- ); -}; - -export default UpcomingDishes; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx b/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx deleted file mode 100644 index b82fc1b..0000000 --- a/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {FC, FormEvent, useMemo, useState} from "react"; -import {DishType} from "@/types/DishType"; -import {ScheduledUserDishType} from "@/types/ScheduledUserDishType"; -import {updateScheduledUserDish} from "@/utils/api/scheduledUserDishesApi"; -import Alert from "@/components/ui/Alert"; -import classNames from "classnames"; - -interface Props { - scheduledUserDish: ScheduledUserDishType - allDishes: DishType[] -} - -const UserDishEditCard: FC = ({ scheduledUserDish, allDishes }) => { - const [selectedUserDishId, setSelectedUserDishId] = useState(scheduledUserDish.user_dish ? scheduledUserDish.user_dish.id : 0) - const [errorMessage, setErrorMessage] = useState("") - const [isSuccess, setIsSuccess] = useState(false); - - const selectStyle = classNames( - 'p-2', 'rounded', 'w-full', 'background-secondary', - 'focus:outline-none', - 'transition-[border-color] ease-out duration-1000', 'border-2', // Keep consistent base styles - { - 'border-green-500': isSuccess, // Green border when successful - 'border-red-500': !isSuccess && errorMessage !== "", // Red border when there's an error - 'border-secondary': !isSuccess && errorMessage === "", // Default border for neutral state - } - ) - - const handleOnChange = (e: FormEvent) => { - const userDishId = parseInt(e.currentTarget.value); - setSelectedUserDishId(userDishId); - - updateScheduledUserDish(scheduledUserDish.id, userDishId) - .then(() => { - setIsSuccess(false); - setTimeout(() => { - setIsSuccess(true); - setTimeout(() => setIsSuccess(false), 1000); - }, 0); - }) - .catch((error) => { - setErrorMessage(error); // Log API errors - }); - }; - - const filteredDishes = useMemo(() => - allDishes.filter((dish: DishType) => - dish.users.some((user) => user.id === scheduledUserDish.user_dish.user.id) - ), - [allDishes, scheduledUserDish.user_dish.user.id] - ) - - return ( -
-
{scheduledUserDish.user_dish.user.name}
- - { errorMessage !== "" && { errorMessage } } - - - -
- ); -}; - -export default UserDishEditCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx deleted file mode 100644 index 9a61d87..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {DateTime} from "luxon"; -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - date: string - className?: string; -} - -const DateBadge: FC = ({ className, date }) => { - const isToday = DateTime.fromISO(date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const textStyle = classNames("inline font-bold", { - 'text-accent-blue': isToday, - 'text-secondary': !isToday, - }, className) - - return ( -
-
{DateTime.fromISO(date).toFormat("dd")}
-
-
{DateTime.fromISO(date).toFormat("LLL")}
-
- ) -} - -export default DateBadge \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx deleted file mode 100644 index 77c68e0..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, {FC} from "react"; -import {UserType} from "@/types/UserType"; -import ScheduleDayCardUserDish from "@/components/features/schedule/dayCard/ScheduleDayCardUserDish"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import Link from "next/link"; -import {PencilSquareIcon} from "@heroicons/react/24/outline"; -import useRoutes from "@/hooks/useRoutes"; -import DateBadge from "@/components/features/schedule/dayCard/DateBadge"; -import { DateTime } from "luxon" -import classNames from "classnames" - -interface Props { - schedule: ScheduleType|FilledScheduleType; - users: UserType[]; -} - -const ScheduleDayCard: FC = ({schedule, users}) => { - const routes = useRoutes() - const isToday = DateTime.fromISO(schedule.date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const containerStyles = classNames( - 'w-full bg-gray-500 pt-5 pb-2 rounded-2xl text-xl', { - 'border-2 text-accent-blue border-accent-blue': isToday, - } - ) - - return ( -
- - -
- { - users.map((user) => ) - } - -
- - Edit - -
-
-
- ); -}; - -export default ScheduleDayCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx deleted file mode 100644 index 64f0f0e..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC } from "react"; -import { ScheduledUserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; - -interface Props { - schedule: ScheduleType|FilledScheduleType; - user: UserType; -} - -const ScheduleDayCardUserDish: FC = ({ schedule, user }) => { - const getDish = (user: UserType) => { - const scheduled_dishes = schedule.scheduled_user_dishes.filter((scheduled_user_dish: ScheduledUserDishType) => ( - scheduled_user_dish.user_dish?.user.id == user.id - )) - - if (scheduled_dishes.length > 0) { - return scheduled_dishes[0].user_dish.dish.name - } - - return '/' - } - - return ( -
-
{ user.name } :
-
{ getDish(user) }
-
- ); -}; - -export default ScheduleDayCardUserDish; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/users/EditUserForm.tsx b/frontend-old/archive/src/components/features/users/EditUserForm.tsx deleted file mode 100644 index 1593dd9..0000000 --- a/frontend-old/archive/src/components/features/users/EditUserForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {FC, useState} from "react"; -import {useRouter} from "next/navigation"; -import useRoutes from "@/hooks/useRoutes"; -import {updateUser} from "@/utils/api/usersApi"; -import PageTitle from "@/components/ui/PageTitle"; -import Link from "next/link"; -import Alert from "@/components/ui/Alert"; -import {UserType} from "@/types/UserType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - user: UserType; -} - -const EditUserForm: FC = ({ user }) => { - - const [name, setName] = useState(user.name); - const [error, setError] = useState(''); - const router = useRouter(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - // validateName - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - updateUser(user, name) - .then(() => { - router.push(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Update -
-
- ); -} - -export default EditUserForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/AuthGuard.tsx b/frontend-old/archive/src/components/layout/AuthGuard.tsx deleted file mode 100644 index c4e0482..0000000 --- a/frontend-old/archive/src/components/layout/AuthGuard.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import { useAuth } from '@/context/AuthContext'; -import { useRouter, usePathname } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; - -// Optional Loading spinner component to display while loading -const LoadingSpinner = () => ( -
-
-
-); - -export default function AuthGuard({ children }: { children: React.ReactNode }) { - const { isAuthenticated } = useAuth(); // Access the authentication state from AuthContext - const router = useRouter(); - const pathname = usePathname(); - const [loading, setLoading] = useState(true); - - // Define public routes that can be accessed without authentication - const publicRoutes = ['/login', '/register']; - const isPublic = publicRoutes.includes(pathname); - - useEffect(() => { - // Determine behavior based on auth state and route type - if (isAuthenticated === null) { - // Await authentication resolution (e.g., token check) - setLoading(true); - } else if (isAuthenticated && isPublic) { - // Redirect authenticated users away from public pages - router.replace('/'); - } else if (!isAuthenticated && !isPublic) { - // Redirect unauthenticated users trying to access protected pages - router.replace('/login'); - } else { - // Otherwise, stop loading since the state is resolved - setLoading(false); - } - }, [isAuthenticated, pathname, isPublic, router]); - - // Show a spinner while authentication state is loading - if (loading) { - return ; - } - - // Render children only when the authentication state and path are valid - return <>{children}; -} \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/Card.tsx b/frontend-old/archive/src/components/layout/Card.tsx deleted file mode 100644 index 6784526..0000000 --- a/frontend-old/archive/src/components/layout/Card.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, {FC} from "react"; - -interface Props { - children: React.ReactNode; -} - -const Card: FC = ({ children }) => { - return ( -
- { children } -
- ) -} - -export default Card \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/NavBar.tsx b/frontend-old/archive/src/components/layout/NavBar.tsx deleted file mode 100644 index 67cadb2..0000000 --- a/frontend-old/archive/src/components/layout/NavBar.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import React, { useState } from "react"; -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import MobileDropdownMenu from "@/components/features/navbar/MobileDropdownMenu"; -import {useRouter} from "next/navigation"; -import {useAuth} from "@/context/AuthContext"; - -const NavBar = () => { - const [isOpen, setIsOpen] = useState(false); - const routes = useRoutes(); - const router = useRouter(); - const {isAuthenticated, logout} = useAuth(); - - const handleLogout = (e: React.MouseEvent) => { - e.preventDefault(); - logout(); - router.replace('/login'); - }; - - return ( - - ); -}; - -export default NavBar; \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Alert.tsx b/frontend-old/archive/src/components/ui/Alert.tsx deleted file mode 100644 index c32070c..0000000 --- a/frontend-old/archive/src/components/ui/Alert.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - type: 'error' | 'warning' | 'info' | 'success'; -} - -const Alert: FC = ({ children, className, type } ) => { - let bgColor = 'bg-blue-200' - let fgColor = 'bg-blue-800' - - if (type == 'error') { - bgColor = 'bg-red-200' - fgColor = 'bg-red-800' - } else if (type == 'warning') { - bgColor = 'bg-orange-200' - fgColor = 'bg-orange-800' - } else if (type == 'success') { - bgColor = 'border-2 border-green-500' - fgColor = 'text-green-500' - } - - const styles = classNames(fgColor, bgColor, className, 'rounded') - - return ( -
- { children} -
- ) -} - -export default Alert \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Button.tsx b/frontend-old/archive/src/components/ui/Button.tsx deleted file mode 100644 index 4ad006c..0000000 --- a/frontend-old/archive/src/components/ui/Button.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Link from "next/link"; -import React, { FC, ReactElement, ReactNode } from "react"; -import classNames from "classnames"; - -interface ButtonProps { - appearance?: 'solid' | 'outline' | 'text'; - children: ReactNode; - className?: string; - href?: string; - icon?: ReactNode; - onClick?: () => void; - disabled?: boolean; - size?: 'small' | 'medium' | 'large'; - type?: 'button' | 'submit' | 'reset'; - variant?: 'primary' | 'secondary' | 'accent'; -} - -const Button: FC = ({ appearance, children, className, disabled, href, icon, onClick, - size = 'medium', type, - variant = 'primary' -}) => { - const styles = classNames( - "flex items-center space-x-1", - "justify-center font-size-18 py-2 px-4 rounded flex", - { - 'border-2 border-primary background-red text-white': variant === 'primary' && appearance === 'solid', - 'border-2 border-primary text-primary': variant === 'primary' && appearance === 'outline', - 'text-primary': variant === 'primary' && appearance === 'text', - 'border-2 border-secondary text-secondary': variant === 'secondary' && appearance === 'outline', - 'border-2 border-accent-blue text-accent-blue': variant === 'accent' && appearance === 'outline', - }, - className - ) - - const iconClassNames = classNames({ - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - if (href !== undefined) { - return ( - - { icon && iconElement} - { children} - - ) - } - - return -} - -export default Button \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx b/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx deleted file mode 100644 index b668b6b..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { FC } from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const OutlineButton: FC = ({ children, className, disabled = false, onClick, size, type }) => { - const style = classNames( - "justify-center border-2 border-accent font-size-18 text-accent-blue py-2 px-4 rounded flex", - { 'text-xs': size === "small" }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default OutlineButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx b/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx deleted file mode 100644 index ab0e9b1..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { FC, ReactElement } from "react"; -import classNames from "classnames"; -import Link from "next/link"; - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const OutlineLinkButton: FC = ({ children, className, href, icon, size = "medium", variant }) => { - const linkClassNames = classNames( - "underline font-default pt-3 pb-3 px-4 rounded mb-0 flex", - { - 'text-primary border-primary': variant === "primary", - 'text-secondary border-secondary': variant === "secondary", - 'text-accent-blue border-accent': !variant || !["primary", "secondary"].includes(variant), - }, { - 'text-size-14': size === "small", - 'font-size-18': !size || size === "medium", - 'text-2xl': size === "large", - }, - className, - ) - - const iconClassNames = classNames("mt-0.5", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - - {iconElement} - {children} - - ) -} - -export default OutlineLinkButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx b/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx deleted file mode 100644 index 62ac01d..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const SolidButton: FC = ({ children, className, disabled = false, onClick, size, type }) => { - const style = classNames( - "py-2 px-4 bg-primary text-white text-xl p-2 rounded hover:bg-secondary mb-0", - { - 'text-xs' : size === "small", - 'font-size-18' : !size || size === "medium", - }, - className - ) - - if (onClick === undefined) { - onClick = () => {} - } - - return ( - - ) -} - -export default SolidButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx b/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx deleted file mode 100644 index 77a78dd..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { FC, ReactElement } from "react"; -import classNames from "classnames"; -import Link from "next/link"; - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const SolidLinkButton: FC = ({ children, className, href, icon, size = "medium", variant }) => { - const style = classNames( - "py-2 px-4 text-xl p-2 rounded hover:bg-secondary mb-0 text-center flex", - { - 'background-red text-white': variant === "primary", - 'background-secondary border-2 border-secondary': variant === "secondary", - }, - className - ) - - const iconClassNames = classNames("mt-1", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - -
- {iconElement} - {children} -
- - ) -} - -export default SolidLinkButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Description.tsx b/frontend-old/archive/src/components/ui/Description.tsx deleted file mode 100644 index 8b429c0..0000000 --- a/frontend-old/archive/src/components/ui/Description.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -interface Props { - children: React.ReactNode; - className?: string; -} - -const Description = ({ children, className }: Props) => { - const style = classNames("italic font-size-16", - className - ) - - return

{ children }

-} - -export default Description \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Hr.tsx b/frontend-old/archive/src/components/ui/Hr.tsx deleted file mode 100644 index 59f0d2d..0000000 --- a/frontend-old/archive/src/components/ui/Hr.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { FC } from "react" -import classNames from "classnames" - -interface HrProps { - className?: string; -} - -const Hr: FC = ({ className }) => { - const styles = classNames("my-4 border-secondary", className) - - return
-} - -export default Hr \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Label.tsx b/frontend-old/archive/src/components/ui/Label.tsx deleted file mode 100644 index 80bea0b..0000000 --- a/frontend-old/archive/src/components/ui/Label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, {FC, ReactNode} from "react"; - -interface LabelProps { - href?: string; - children: ReactNode; - onClick?: () => void; -} - -const Label: FC = ({ href, children, onClick }) => { - const styles = "items-center space-x-1 background-accent p-2 rounded" - - if (href !== undefined) { - return ( -
- { children} -
- ) - } - - return -} - -export default Label \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Modal.tsx b/frontend-old/archive/src/components/ui/Modal.tsx deleted file mode 100644 index e6e5c9c..0000000 --- a/frontend-old/archive/src/components/ui/Modal.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import {FC, JSX} from "react"; -import {Dialog, DialogBackdrop, DialogPanel} from "@headlessui/react"; -import classNames from "classnames"; -import {XMarkIcon} from "@heroicons/react/24/outline"; -import Button from "@/components/ui/Button" - -interface ModalProps { - buttonChildren?: JSX.Element; - buttonClassName?: string; - buttonLabel?: string; - modalChildren: JSX.Element; - modalOpen?: boolean; - setModalOpen: (open: boolean) => void; -} - -const Modal: FC = ({ - buttonLabel, - buttonClassName, - modalChildren, - modalOpen, - buttonChildren, - setModalOpen, -}) => { - const buttonStyles = classNames(buttonClassName, 'anta-regular'); - - const closeModal = () => { - setModalOpen(false) - } - - return ( - <> - - - - -
-
- - closeModal()}/> - {modalChildren} - -
-
-
- - ) -} - -export default Modal; \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/PageTitle.tsx b/frontend-old/archive/src/components/ui/PageTitle.tsx deleted file mode 100644 index ca65f18..0000000 --- a/frontend-old/archive/src/components/ui/PageTitle.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import classNames from "classnames"; -import {FC} from "react"; - -interface Props { - children: string, - className?: string, -} - -const PageTitle: FC = ({ children, className }) => { - const styles = classNames( - 'ml-4 text-2xl font-default uppercase w-full text-accent-blue font-bold', - className, - ) - - return

{ children }

-} - -export default PageTitle \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/RecurrenceInput.tsx b/frontend-old/archive/src/components/ui/RecurrenceInput.tsx deleted file mode 100644 index 756715d..0000000 --- a/frontend-old/archive/src/components/ui/RecurrenceInput.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, {FC, useState} from "react"; - -interface Props { - value: number; - setValue: (value: number) => void; -} - -const RecurrenceInput: FC = ({ value, setValue}) => { - const [openInput, setOpenInput] = useState<'category' | 'number'>([7, 365].includes(value) ? 'category' : 'number') - - const toggleInput = (e: React.MouseEvent) => { - e.preventDefault() - setOpenInput(openInput == 'category' ? 'number' : 'category') - } - - const toggleButton = () => { - return ( - - ) - } - - const prepareValue = (v: string) => { - setValue(parseInt(v)) - } - - return ( -
-
- - - { toggleButton() } -
- -
- - prepareValue(e.target.value)} - className="p-2 border rounded w-full bg-gray-500 border-secondary" - /> - { toggleButton() } -
-
- ) -} - -export default RecurrenceInput \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/SectionTitle.tsx b/frontend-old/archive/src/components/ui/SectionTitle.tsx deleted file mode 100644 index 93d13c4..0000000 --- a/frontend-old/archive/src/components/ui/SectionTitle.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string; - className?: string; -} - -const SectionTitle = ({ children, className }: Props) => { - const style = classNames("block font-size-18 uppercase w-full pl-2 text-accent-blue", - className - ) - - return

{ children }

-} - -export default SectionTitle \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Toggle.tsx b/frontend-old/archive/src/components/ui/Toggle.tsx deleted file mode 100644 index 786b093..0000000 --- a/frontend-old/archive/src/components/ui/Toggle.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import {FC} from "react"; - -interface ToggleProps { - checked: boolean; - onChange: (checked: boolean) => void; -} - -const Toggle: FC = ({ checked, onChange }) => { - const handleChange = () => { - onChange(checked); - } - - return ( - - ); -}; - -export default Toggle; \ No newline at end of file diff --git a/frontend-old/archive/src/context/AuthContext.tsx b/frontend-old/archive/src/context/AuthContext.tsx deleted file mode 100644 index 8d77eb3..0000000 --- a/frontend-old/archive/src/context/AuthContext.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client" - -import React, { createContext, useContext, useEffect, useState } from 'react'; - -interface AuthContextProps { - isAuthenticated: boolean | null; - login: () => void; - logout: () => void; -} - -const AuthContext = createContext({ - isAuthenticated: null, - login: () => {}, - logout: () => {}, -}); - -export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - const [isAuthenticated, setIsAuthenticated] = useState(null); - - useEffect(() => { - const token = localStorage.getItem('token'); - if (token) { - // You could add any token validation logic here - setIsAuthenticated(true); - } else { - setIsAuthenticated(false); - } - }, []); - - const login = () => { - setIsAuthenticated(true); - }; - - const logout = () => { - setIsAuthenticated(false); - localStorage.removeItem('token'); - }; - - return ( - - {children} - - ); -}; - -export const useAuth = () => useContext(AuthContext); \ No newline at end of file diff --git a/frontend-old/archive/src/helpers/Date.ts b/frontend-old/archive/src/helpers/Date.ts deleted file mode 100644 index f183c86..0000000 --- a/frontend-old/archive/src/helpers/Date.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DateTime } from 'luxon'; - -// Validate if a given string matches the "yyyy-MM-dd" format and is a valid date -export const isValidDate = (date: string): boolean => { - const parsedDate = DateTime.fromFormat(date, 'yyyy-MM-dd'); - return parsedDate.isValid && parsedDate.toFormat('yyyy-MM-dd') === date; -}; - -// Format a date to a specific string format -export const formatDate = (date: Date | string, format: string = 'yyyy-MM-dd'): string => { - const parsedDate = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date); - return parsedDate.toFormat(format); -}; - -// Compare two dates to see if one is before the other -export const isBefore = (date1: string, date2: string): boolean => { - return DateTime.fromISO(date1) < DateTime.fromISO(date2); -}; diff --git a/frontend-old/archive/src/hooks/useFetchDishes.ts b/frontend-old/archive/src/hooks/useFetchDishes.ts deleted file mode 100644 index a7ab747..0000000 --- a/frontend-old/archive/src/hooks/useFetchDishes.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import { listDishes } from "@/utils/api/dishApi" -import { DishType } from "@/types/DishType" - -export const useFetchDishes = () => { - const [dishes, setDishes] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchDishes = async () => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchDishes(); - }, []); - - return { dishes, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/hooks/useFetchUsers.ts b/frontend-old/archive/src/hooks/useFetchUsers.ts deleted file mode 100644 index f6df05b..0000000 --- a/frontend-old/archive/src/hooks/useFetchUsers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import {UserType} from "@/types/UserType"; -import {listUsers} from "@/utils/api/usersApi"; - -export const useFetchUsers = () => { - const [users, setUsers] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchUsers = async () => { - listUsers() - .then((users: UserType[]) => setUsers(users)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchUsers(); - }, []); - - return { users, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/hooks/useRoutes.ts b/frontend-old/archive/src/hooks/useRoutes.ts deleted file mode 100644 index 7120da4..0000000 --- a/frontend-old/archive/src/hooks/useRoutes.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {UserType} from "@/types/UserType"; - -const useRoutes = () => { - return { - home: () => "/", - auth: { - login: () => "/login", - register: () => "/register", - }, - dish: { - index: () => "/dishes", - create: () => "/dishes/create", - edit: (dish: DishType) => `/dishes/${dish.id}/edit`, - delete: (dish: DishType) => `/dishes/${dish.id}/delete`, - }, - schedule: { - date: { - edit: (date: string) => `/schedule/${date}/edit` - }, - history: () => "/scheduled-user-dishes/history", - }, - user: { - index: () => "/users", - create: () => `/users/create`, - edit: (user: UserType) => `/users/${user.id}/edit`, - delete: (user: UserType) => `/users/${user.id}/delete`, - } - }; -}; - -export default useRoutes; \ No newline at end of file diff --git a/frontend-old/archive/src/styles/base/globals.css b/frontend-old/archive/src/styles/base/globals.css deleted file mode 100644 index 918cbfb..0000000 --- a/frontend-old/archive/src/styles/base/globals.css +++ /dev/null @@ -1,19 +0,0 @@ -html, body { - margin: 0; - padding: 0; - width: 100%; - overflow-x: hidden; -} - -body { - font-family: Arial, Helvetica, sans-serif; -} - - -.toggle-input:checked { - background-color: #22c55e; /* bg-green-500 */ -} - -.toggle-input:checked ~ span:last-child { - --tw-translate-x: 1.75rem; /* translate-x-7 */ -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/components/buttons.css b/frontend-old/archive/src/styles/components/buttons.css deleted file mode 100644 index 07fc4de..0000000 --- a/frontend-old/archive/src/styles/components/buttons.css +++ /dev/null @@ -1,42 +0,0 @@ -.button-primary-solid { - background-color: var(--color-primary); - color: var(--color-secondary-200); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} -.button-primary-outline { - background-color: var(--color-background); - color: var(--color-primary); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} - -.button-secondary-solid { - background-color: var(--color-secondary); - color: var(--color-primary); - border: 1px solid var(--color-secondary); -} - -.button-accent-solid { - background-color: var(--color-accent-blue); - color: var(--color-secondary-900); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline { - background-color: var(--color-background); - color: var(--color-accent-blue); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline:hover { - background-color: var(--color-background-400); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/components/select.css b/frontend-old/archive/src/styles/components/select.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend-old/archive/src/styles/main.css b/frontend-old/archive/src/styles/main.css deleted file mode 100644 index 5ca69ea..0000000 --- a/frontend-old/archive/src/styles/main.css +++ /dev/null @@ -1,10 +0,0 @@ -@import "./theme/borders.css"; -@import "./theme/fonts.css"; -@import "./components/buttons.css"; - -@import "./base/globals.css"; -@import "./theme/colors.css"; - -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/frontend-old/archive/src/styles/theme/borders.css b/frontend-old/archive/src/styles/theme/borders.css deleted file mode 100644 index b4ed5db..0000000 --- a/frontend-old/archive/src/styles/theme/borders.css +++ /dev/null @@ -1,14 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} -.border-accent-800 { - border-color: var(--color-accent-blue-800); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors.css b/frontend-old/archive/src/styles/theme/colors.css deleted file mode 100644 index 338bcd7..0000000 --- a/frontend-old/archive/src/styles/theme/colors.css +++ /dev/null @@ -1,10 +0,0 @@ -@import './colors/root.css'; -@import 'colors/background.css'; -@import 'colors/border.css'; -@import 'colors/text.css'; - -body { - color: var(--color-secondary) !important; - background: var(--color-gray-600) !important; -} - diff --git a/frontend-old/archive/src/styles/theme/colors/background.css b/frontend-old/archive/src/styles/theme/colors/background.css deleted file mode 100644 index e8548f5..0000000 --- a/frontend-old/archive/src/styles/theme/colors/background.css +++ /dev/null @@ -1,226 +0,0 @@ -.bg-gray-100 { - background-color: var(--color-gray-100) !important; -} -.bg-gray-200 { - background-color: var(--color-gray-200) !important; -} -.bg-gray-300 { - background-color: var(--color-gray-300) !important; -} -.bg-gray-400 { - background-color: var(--color-gray-400) !important; -} -.bg-gray-500 { - background-color: var(--color-gray-500) !important; -} -.bg-gray-600 { - background-color: var(--color-gray-600) !important; -} -.bg-gray-700 { - background-color: var(--color-gray-700) !important; -} -.bg-gray-800 { - background-color: var(--color-gray-800) !important; -} -.bg-gray-900 { - background-color: var(--color-gray-900) !important; -} - - -.bg-primary { - background-color: var(--color-primary) !important; -} - - -.bg-accent-blue { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-100 { - background-color: var(--color-accent-blue-100) !important; -} -.bg-accent-blue-200 { - background-color: var(--color-accent-blue-200) !important; -} -.bg-accent-blue-300 { - background-color: var(--color-accent-blue-300) !important; -} -.bg-accent-blue-400 { - background-color: var(--color-accent-blue-400) !important; -} -.bg-accent-blue-500 { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-600 { - background-color: var(--color-accent-blue-600) !important; -} -.bg-accent-blue-700 { - background-color: var(--color-accent-blue-700) !important; -} -.bg-accent-blue-800 { - background-color: var(--color-accent-blue-800) !important; -} -.bg-accent-blue-900 { - background-color: var(--color-accent-blue-900) !important; -} - - -.bg-accent-yellow { - background-color: var(--color-accent-yellow) !important; -} - -.bg-accent-yellow-100 { - background-color: var(--color-accent-yellow-100) !important; -} - -.bg-accent-yellow-200 { - background-color: var(--color-accent-yellow-200) !important; -} - -.bg-accent-yellow-300 { - background-color: var(--color-accent-yellow-300) !important; -} - -.bg-accent-yellow-400 { - background-color: var(--color-accent-yellow-400) !important; -} - -.bg-accent-yellow-500 { - background-color: var(--color-accent-yellow-500) !important; -} - -.bg-accent-yellow-600 { - background-color: var(--color-accent-yellow-600) !important; -} - -.bg-accent-yellow-700 { - background-color: var(--color-accent-yellow-700) !important; -} - -.bg-accent-yellow-800 { - background-color: var(--color-accent-yellow-800) !important; -} - -.bg-accent-yellow-900 { - background-color: var(--color-accent-yellow-900) !important; -} - - -.bg-success { - background-color: var(--color-success) !important; -} - -.bg-success-100 { - background-color: var(--color-success-100) !important; -} - -.bg-success-200 { - background-color: var(--color-success-200) !important; -} - -.bg-success-300 { - background-color: var(--color-success-300) !important; -} - -.bg-success-400 { - background-color: var(--color-success-400) !important; -} - -.bg-success-500 { - background-color: var(--color-success-500) !important; -} - -.bg-success-600 { - background-color: var(--color-success-600) !important; -} - -.bg-success-700 { - background-color: var(--color-success-700) !important; -} - -.bg-success-800 { - background-color: var(--color-success-800) !important; -} - -.bg-success-900 { - background-color: var(--color-success-900) !important; -} - -.bg-warning { - background-color: var(--color-warning) !important; -} - -.bg-warning-100 { - background-color: var(--color-warning-100) !important; -} - -.bg-warning-200 { - background-color: var(--color-warning-200) !important; -} - -.bg-warning-300 { - background-color: var(--color-warning-300) !important; -} - -.bg-warning-400 { - background-color: var(--color-warning-400) !important; -} - -.bg-warning-500 { - background-color: var(--color-warning-500) !important; -} - -.bg-warning-600 { - background-color: var(--color-warning-600) !important; -} - -.bg-warning-700 { - background-color: var(--color-warning-700) !important; -} - -.bg-warning-800 { - background-color: var(--color-warning-800) !important; -} - -.bg-warning-900 { - background-color: var(--color-warning-900) !important; -} - -.bg-danger { - background-color: var(--color-danger) !important; -} - -.bg-danger-100 { - background-color: var(--color-danger-100) !important; -} - -.bg-danger-200 { - background-color: var(--color-danger-200) !important; -} - -.bg-danger-300 { - background-color: var(--color-danger-300) !important; -} - -.bg-danger-400 { - background-color: var(--color-danger-400) !important; -} - -.bg-danger-500 { - background-color: var(--color-danger-500) !important; -} - -.bg-danger-600 { - background-color: var(--color-danger-600) !important; -} - -.bg-danger-700 { - background-color: var(--color-danger-700) !important; -} - -.bg-danger-800 { - background-color: var(--color-danger-800) !important; -} - -.bg-danger-900 { - background-color: var(--color-danger-900) !important; -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors/border.css b/frontend-old/archive/src/styles/theme/colors/border.css deleted file mode 100644 index 8975296..0000000 --- a/frontend-old/archive/src/styles/theme/colors/border.css +++ /dev/null @@ -1,286 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-primary-100 { - border-color: var(--color-primary-100); -} - -.border-primary-200 { - border-color: var(--color-primary-200); -} - -.border-primary-300 { - border-color: var(--color-primary-300); -} - -.border-primary-400 { - border-color: var(--color-primary-400); -} - -.border-primary-500 { - border-color: var(--color-primary-500); -} - -.border-primary-600 { - border-color: var(--color-primary-600); -} - -.border-primary-700 { - border-color: var(--color-primary-700); -} - -.border-primary-800 { - border-color: var(--color-primary-800); -} - -.border-primary-900 { - border-color: var(--color-primary-900); -} - - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-secondary-100 { - border-color: var(--color-secondary-100); -} - -.border-secondary-200 { - border-color: var(--color-secondary-200); -} - -.border-secondary-300 { - border-color: var(--color-secondary-300); -} - -.border-secondary-400 { - border-color: var(--color-secondary-400); -} - -.border-secondary-500 { - border-color: var(--color-secondary-500); -} - -.border-secondary-600 { - border-color: var(--color-secondary-600); -} - -.border-secondary-700 { - border-color: var(--color-secondary-700); -} - -.border-secondary-800 { - border-color: var(--color-secondary-800); -} - -.border-secondary-900 { - border-color: var(--color-secondary-900); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} - -.border-accent-blue-100 { - border-color: var(--color-accent-blue-100); -} - -.border-accent-blue-200 { - border-color: var(--color-accent-blue-200); -} - -.border-accent-blue-300 { - border-color: var(--color-accent-blue-300); -} - -.border-accent-blue-400 { - border-color: var(--color-accent-blue-400); -} - -.border-accent-blue-500 { - border-color: var(--color-accent-blue-500); -} - -.border-accent-blue-600 { - border-color: var(--color-accent-blue-600); -} - -.border-accent-blue-700 { - border-color: var(--color-accent-blue-700); -} - -.border-accent-blue-800 { - border-color: var(--color-accent-blue-800); -} - -.border-accent-blue-900 { - border-color: var(--color-accent-blue-900); -} - - -.border-accent-yellow { - border-color: var(--color-accent-yellow); -} - -.border-accent-yellow-100 { - border-color: var(--color-accent-yellow-100); -} - -.border-accent-yellow-200 { - border-color: var(--color-accent-yellow-200); -} - -.border-accent-yellow-300 { - border-color: var(--color-accent-yellow-300); -} - -.border-accent-yellow-400 { - border-color: var(--color-accent-yellow-400); -} - -.border-accent-yellow-500 { - border-color: var(--color-accent-yellow-500); -} - -.border-accent-yellow-600 { - border-color: var(--color-accent-yellow-600); -} - -.border-accent-yellow-700 { - border-color: var(--color-accent-yellow-700); -} - -.border-accent-yellow-800 { - border-color: var(--color-accent-yellow-800); -} - -.border-accent-yellow-900 { - border-color: var(--color-accent-yellow-900); -} - - -.border-background { - border-color: var(--color-background) !important; -} - -.border-danger { - border-color: var(--color-danger); -} - -.border-danger-100 { - border-color: var(--color-danger-100); -} - -.border-danger-200 { - border-color: var(--color-danger-200); -} - -.border-danger-300 { - border-color: var(--color-danger-300); -} - -.border-danger-400 { - border-color: var(--color-danger-400); -} - -.border-danger-500 { - border-color: var(--color-danger-500); -} - -.border-danger-600 { - border-color: var(--color-danger-600); -} - -.border-danger-700 { - border-color: var(--color-danger-700); -} - -.border-danger-800 { - border-color: var(--color-danger-800); -} - -.border-danger-900 { - border-color: var(--color-danger-900); -} - -.border-success { - border-color: var(--color-success); -} - -.border-success-100 { - border-color: var(--color-success-100); -} - -.border-success-200 { - border-color: var(--color-success-200); -} - -.border-success-300 { - border-color: var(--color-success-300); -} - -.border-success-400 { - border-color: var(--color-success-400); -} - -.border-success-500 { - border-color: var(--color-success-500); -} - -.border-success-600 { - border-color: var(--color-success-600); -} - -.border-success-700 { - border-color: var(--color-success-700); -} - -.border-success-800 { - border-color: var(--color-success-800); -} - -.border-success-900 { - border-color: var(--color-success-900); -} - -.border-warning { - border-color: var(--color-warning); -} - -.border-warning-100 { - border-color: var(--color-warning-100); -} - -.border-warning-200 { - border-color: var(--color-warning-200); -} - -.border-warning-300 { - border-color: var(--color-warning-300); -} - -.border-warning-400 { - border-color: var(--color-warning-400); -} - -.border-warning-500 { - border-color: var(--color-warning-500); -} - -.border-warning-600 { - border-color: var(--color-warning-600); -} - -.border-warning-700 { - border-color: var(--color-warning-700); -} - -.border-warning-800 { - border-color: var(--color-warning-800); -} - -.border-warning-900 { - border-color: var(--color-warning-900); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors/root.css b/frontend-old/archive/src/styles/theme/colors/root.css deleted file mode 100644 index acffac0..0000000 --- a/frontend-old/archive/src/styles/theme/colors/root.css +++ /dev/null @@ -1,193 +0,0 @@ -:root { - --color-rose-50: #FFF5FC; - --color-rose-100: #FCE6F5; - --color-rose-200: #FAC3E7; - --color-rose-300: #F7A1D5; - --color-rose-400: #F25EAB; - --color-rose-500: #ED1F79; - --color-rose-600: #D61A68; - --color-rose-700: #B3124F; - --color-rose-800: #8F0B39; - --color-rose-900: #6B0626; - --color-rose-950: #450315; - - --color-deluge-50: #FAF7FC; - --color-deluge-100: #F2EDF7; - --color-deluge-200: #E2DAF0; - --color-deluge-300: #CEC3E6; - --color-deluge-400: #A49BD1; - --color-deluge-500: #7776BC; - --color-deluge-600: #6361AB; - --color-deluge-700: #43428C; - --color-deluge-800: #2C2B70; - --color-deluge-900: #191854; - --color-deluge-950: #0A0A36; - - --color-malibu-50: #FAFEFF; - --color-malibu-100: #F5FDFF; - --color-malibu-200: #E1F6FC; - --color-malibu-300: #CDEDFA; - --color-malibu-400: #ABDEF7; - --color-malibu-500: #85C7F2; - --color-malibu-600: #6EACDB; - --color-malibu-700: #4A81B5; - --color-malibu-800: #305F91; - --color-malibu-900: #1B3F6E; - --color-malibu-950: #0B2247; - - --color-gamboge-50: #FFFDF2; - --color-gamboge-100: #FCF7E3; - --color-gamboge-200: #FAECBB; - --color-gamboge-300: #F5DC93; - --color-gamboge-400: #EDBB47; - --color-gamboge-500: #E59500; - --color-gamboge-600: #CF7F00; - --color-gamboge-700: #AB6100; - --color-gamboge-800: #8A4700; - --color-gamboge-900: #663000; - --color-gamboge-950: #421C00; - - --color-ebony-clay-100: #9AA2B3; /* Soft slate */ - --color-ebony-clay-200: #7A8093; /* Balanced midtone */ - --color-ebony-clay-300: #5D637A; /* Former 400 */ - --color-ebony-clay-400: #444760; /* New shadowed steel */ - --color-ebony-clay-500: #2B2C41; - --color-ebony-clay-600: #24263C; /* Adjusted — less jumpy */ - --color-ebony-clay-700: #1D1E36; /* Interpolated midpoint */ - --color-ebony-clay-800: #131427; /* Slightly lifted from old 800 */ - --color-ebony-clay-900: #0A0B1C; - --color-ebony-clay-950: #030412; - - --color-alizarin-crimson-50: #FFF5FA; - --color-alizarin-crimson-100: #FCE6F1; - --color-alizarin-crimson-200: #FAC3DC; - --color-alizarin-crimson-300: #F59FC0; - --color-alizarin-crimson-400: #F05D82; - --color-alizarin-crimson-500: #E71D36; - --color-alizarin-crimson-600: #D1192F; - --color-alizarin-crimson-700: #AD1121; - --color-alizarin-crimson-800: #8C0B18; - --color-alizarin-crimson-900: #69060E; - --color-alizarin-crimson-950: #420308; - - --color-spring-green-50: #F5FFFC; - --color-spring-green-100: #E8FFF9; - --color-spring-green-200: #C7FFEE; - --color-spring-green-300: #A4FCDF; - --color-spring-green-400: #62FCBC; - --color-spring-green-500: #21FA90; - --color-spring-green-600: #1BE07A; - --color-spring-green-700: #13BA5E; - --color-spring-green-800: #0C9646; - --color-spring-green-900: #07702D; - --color-spring-green-950: #03471A; - - --color-burning-orange-50: #FFFBF5; - --color-burning-orange-100: #FFF7EB; - --color-burning-orange-200: #FFE8CC; - --color-burning-orange-300: #FFD5AD; - --color-burning-orange-400: #FFA973; - --color-burning-orange-500: #FF6B35; - --color-burning-orange-600: #E65A2C; - --color-burning-orange-700: #BF441F; - --color-burning-orange-800: #993114; - --color-burning-orange-900: #731F0A; - --color-burning-orange-950: #4A1004; - - /* Standard naming */ - - --color-primary: var(--color-rose-500); - --color-primary-100: var(--color-rose-100); - --color-primary-200: var(--color-rose-200); - --color-primary-300: var(--color-rose-300); - --color-primary-400: var(--color-rose-400); - --color-primary-500: var(--color-rose-500); - --color-primary-600: var(--color-rose-600); - --color-primary-700: var(--color-rose-700); - --color-primary-800: var(--color-rose-800); - --color-primary-900: var(--color-rose-900); - - --color-secondary: var(--color-deluge-500); - --color-secondary-100: var(--color-deluge-100); - --color-secondary-200: var(--color-deluge-200); - --color-secondary-300: var(--color-deluge-300); - --color-secondary-400: var(--color-deluge-400); - --color-secondary-500: var(--color-deluge-500); - --color-secondary-600: var(--color-deluge-600); - --color-secondary-700: var(--color-deluge-700); - --color-secondary-800: var(--color-deluge-800); - --color-secondary-900: var(--color-deluge-900); - - --color-accent-blue: var(--color-malibu-500); - --color-accent-blue-100: var(--color-malibu-100); - --color-accent-blue-200: var(--color-malibu-200); - --color-accent-blue-300: var(--color-malibu-300); - --color-accent-blue-400: var(--color-malibu-400); - --color-accent-blue-500: var(--color-malibu-500); - --color-accent-blue-600: var(--color-malibu-600); - --color-accent-blue-700: var(--color-malibu-700); - --color-accent-blue-800: var(--color-malibu-800); - --color-accent-blue-900: var(--color-malibu-900); - - --color-accent-yellow: var(--color-gamboge-500); - --color-accent-yellow-50: var(--color-gamboge-50); - --color-accent-yellow-100: var(--color-gamboge-100); - --color-accent-yellow-200: var(--color-gamboge-200); - --color-accent-yellow-300: var(--color-gamboge-300); - --color-accent-yellow-400: var(--color-gamboge-400); - --color-accent-yellow-500: var(--color-gamboge-500); - --color-accent-yellow-600: var(--color-gamboge-600); - --color-accent-yellow-700: var(--color-gamboge-700); - --color-accent-yellow-800: var(--color-gamboge-800); - --color-accent-yellow-900: var(--color-gamboge-900); - --color-accent-yellow-950: var(--color-gamboge-950); - - --color-gray-100: var(--color-ebony-clay-100); - --color-gray-200: var(--color-ebony-clay-200); - --color-gray-300: var(--color-ebony-clay-300); - --color-gray-400: var(--color-ebony-clay-400); - --color-gray-500: var(--color-ebony-clay-500); - --color-gray-600: var(--color-ebony-clay-600); - --color-gray-700: var(--color-ebony-clay-700); - --color-gray-800: var(--color-ebony-clay-800); - --color-gray-900: var(--color-ebony-clay-900); - - --color-danger: var(--color-alizarin-crimson-500); - --color-danger-50: var(--color-alizarin-crimson-50); - --color-danger-100: var(--color-alizarin-crimson-100); - --color-danger-200: var(--color-alizarin-crimson-200); - --color-danger-300: var(--color-alizarin-crimson-300); - --color-danger-400: var(--color-alizarin-crimson-400); - --color-danger-500: var(--color-alizarin-crimson-500); - --color-danger-600: var(--color-alizarin-crimson-600); - --color-danger-700: var(--color-alizarin-crimson-700); - --color-danger-800: var(--color-alizarin-crimson-800); - --color-danger-900: var(--color-alizarin-crimson-900); - --color-danger-950: var(--color-alizarin-crimson-950); - - --color-success: var(--color-spring-green-500); - --color-success-50: var(--color-spring-green-50); - --color-success-100: var(--color-spring-green-100); - --color-success-200: var(--color-spring-green-200); - --color-success-300: var(--color-spring-green-300); - --color-success-400: var(--color-spring-green-400); - --color-success-500: var(--color-spring-green-500); - --color-success-600: var(--color-spring-green-600); - --color-success-700: var(--color-spring-green-700); - --color-success-800: var(--color-spring-green-800); - --color-success-900: var(--color-spring-green-900); - --color-success-950: var(--color-spring-green-950); - - --color-warning: var(--color-burning-orange-500); - --color-warning-50: var(--color-burning-orange-50); - --color-warning-100: var(--color-burning-orange-100); - --color-warning-200: var(--color-burning-orange-200); - --color-warning-300: var(--color-burning-orange-300); - --color-warning-400: var(--color-burning-orange-400); - --color-warning-500: var(--color-burning-orange-500); - --color-warning-600: var(--color-burning-orange-600); - --color-warning-700: var(--color-burning-orange-700); - --color-warning-800: var(--color-burning-orange-800); - --color-warning-900: var(--color-burning-orange-900); - --color-warning-950: var(--color-burning-orange-950); -} diff --git a/frontend-old/archive/src/styles/theme/colors/text.css b/frontend-old/archive/src/styles/theme/colors/text.css deleted file mode 100644 index 1683846..0000000 --- a/frontend-old/archive/src/styles/theme/colors/text.css +++ /dev/null @@ -1,216 +0,0 @@ -.text-primary { - color: var(--color-primary); -} -.text-primary-100 { - color: var(--color-primary-100); -} -.text-primary-200 { - color: var(--color-primary-200); -} -.text-primary-300 { - color: var(--color-primary-300); -} -.text-primary-400 { - color: var(--color-primary-400); -} -.text-primary-500 { - color: var(--color-primary-500); -} -.text-primary-600 { - color: var(--color-primary-600); -} -.text-primary-700 { - color: var(--color-primary-700); -} -.text-primary-800 { - color: var(--color-primary-800); -} -.text-primary-900 { - color: var(--color-primary-900); -} - -.text-secondary { - color: var(--color-secondary); -} -.text-secondary-100 { - color: var(--color-secondary-100); -} -.text-secondary-200 { - color: var(--color-secondary-200); -} -.text-secondary-300 { - color: var(--color-secondary-300); -} -.text-secondary-400 { - color: var(--color-secondary-400); -} -.text-secondary-500 { - color: var(--color-secondary-500); -} -.text-secondary-600 { - color: var(--color-secondary-600); -} -.text-secondary-700 { - color: var(--color-secondary-700); -} -.text-secondary-800 { - color: var(--color-secondary-800); -} -.text-secondary-900 { - color: var(--color-secondary-900); -} - -.text-accent-blue { - color: var(--color-accent-blue); -} -.text-accent-blue-100 { - color: var(--color-accent-blue-100); -} -.text-accent-blue-200 { - color: var(--color-accent-blue-200); -} -.text-accent-blue-300 { - color: var(--color-accent-blue-300); -} -.text-accent-blue-400 { - color: var(--color-accent-blue-400); -} -.text-accent-blue-500 { - color: var(--color-accent-blue-500); -} -.text-accent-blue-600 { - color: var(--color-accent-blue-600); -} -.text-accent-blue-700 { - color: var(--color-accent-blue-700); -} -.text-accent-blue-800 { - color: var(--color-accent-blue-800); -} -.text-accent-blue-900 { - color: var(--color-accent-blue-900); -} - -.text-accent-yellow { - color: var(--color-accent-yellow); -} -.text-accent-yellow-100 { - color: var(--color-accent-yellow-100); -} -.text-accent-yellow-200 { - color: var(--color-accent-yellow-200); -} -.text-accent-yellow-300 { - color: var(--color-accent-yellow-300); -} -.text-accent-yellow-400 { - color: var(--color-accent-yellow-400); -} -.text-accent-yellow-500 { - color: var(--color-accent-yellow-500); -} -.text-accent-yellow-600 { - color: var(--color-accent-yellow-600); -} -.text-accent-yellow-700 { - color: var(--color-accent-yellow-700); -} -.text-accent-yellow-800 { - color: var(--color-accent-yellow-800); -} -.text-accent-yellow-900 { - color: var(--color-accent-yellow-900); -} - -.text-danger { - color: var(--color-danger); -} -.text-danger-100 { - color: var(--color-danger-100); -} -.text-danger-200 { - color: var(--color-danger-200); -} -.text-danger-300 { - color: var(--color-danger-300); -} -.text-danger-400 { - color: var(--color-danger-400); -} -.text-danger-500 { - color: var(--color-danger-500); -} -.text-danger-600 { - color: var(--color-danger-600); -} -.text-danger-700 { - color: var(--color-danger-700); -} -.text-danger-800 { - color: var(--color-danger-800); -} -.text-danger-900 { - color: var(--color-danger-900); -} - -.text-warning { - color: var(--color-warning); -} -.text-warning-100 { - color: var(--color-warning-100); -} -.text-warning-200 { - color: var(--color-warning-200); -} -.text-warning-300 { - color: var(--color-warning-300); -} -.text-warning-400 { - color: var(--color-warning-400); -} -.text-warning-500 { - color: var(--color-warning-500); -} -.text-warning-600 { - color: var(--color-warning-600); -} -.text-warning-700 { - color: var(--color-warning-700); -} -.text-warning-800 { - color: var(--color-warning-800); -} -.text-warning-900 { - color: var(--color-warning-900); -} - -.text-success { - color: var(--color-success); -} -.text-success-100 { - color: var(--color-success-100); -} -.text-success-200 { - color: var(--color-success-200); -} -.text-success-300 { - color: var(--color-success-300); -} -.text-success-400 { - color: var(--color-success-400); -} -.text-success-500 { - color: var(--color-success-500); -} -.text-success-600 { - color: var(--color-success-600); -} -.text-success-700 { - color: var(--color-success-700); -} -.text-success-800 { - color: var(--color-success-800); -} -.text-success-900 { - color: var(--color-success-900); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/fonts.css b/frontend-old/archive/src/styles/theme/fonts.css deleted file mode 100644 index 2646418..0000000 --- a/frontend-old/archive/src/styles/theme/fonts.css +++ /dev/null @@ -1,95 +0,0 @@ -/* Import Space Grotesk from Google Fonts */ -@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&family=Syncopate:wght@400;700&display=swap'); - -/* Global font settings */ - -/* Set Space Grotesk as the default font */ -body { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; - font-size: 14px; - line-height: 1.6; - color: #333; -} - -/* Use Anta for headings */ -h1, h2, h3 { - font-family: 'Syncopate', sans-serif; - color: #111; -} - -/* Use Space Grotesk for smaller text like paragraphs */ -p { - font-family: 'Space Grotesk', sans-serif; -} - - -.font-default { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; -} - -.font-syncopate { - font-family: "Syncopate", serif !important; -} - -.font-space-grotesk { - font-family: 'Space Grotesk', sans-serif; -} - -.font-weight-100 { - font-weight: 100; -} -.font-weight-200 { - font-weight: 200; -} -.font-weight-300 { - font-weight: 300; -} -.font-weight-400 { - font-weight: 400; -} -.font-weight-500 { - font-weight: 500; -} -.font-weight-600 { - font-weight: 600; -} -.font-weight-700 { - font-weight: 700; -} -.font-weight-800 { - font-weight: 800; -} -.font-weight-900 { - font-weight: 900; -} - -.font-size-12 { - font-size: 12px !important; -} - -.font-size-14 { - font-size: 14px !important; -} - -.font-size-16 { - font-size: 16px !important; -} - -.font-size-18 { - font-size: 18px !important; -} - -.font-size-20 { - font-size: 20px !important; -} - -.font-size-24 { - font-size: 24px !important; -} - -.font-size-32 { - font-size: 32px !important; -} -.font-size-48 { - font-size: 48px !important; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/DishType.ts b/frontend-old/archive/src/types/DishType.ts deleted file mode 100644 index e9986c2..0000000 --- a/frontend-old/archive/src/types/DishType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {UserType} from "@/types/UserType"; - -export type DishType = { - id: number - name: string, - recurrence: number, - users: UserType[], -} - -export type DishDateType = { - id: number; - date: string; - dish: DishType; - user: UserType; -} - -export type ScheduledDishesType = { - date: string; - dishes: { dish: DishType, user: UserType }[]; -} diff --git a/frontend-old/archive/src/types/ScheduleType.ts b/frontend-old/archive/src/types/ScheduleType.ts deleted file mode 100644 index 9b03fcf..0000000 --- a/frontend-old/archive/src/types/ScheduleType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -export type RecurrenceType = { - type: "App\\Models\\WeeklyRecurrence" | "App\\Models\\MinimumRecurrence"; - value: number; -} - -export type ScheduleType = { - id: number; - date: string; - scheduled_user_dishes: ScheduledUserDishType[]; - is_skipped: boolean; -} - -export type FilledScheduleType = { - id?: number; - date: string; - is_skipped?: boolean; - scheduled_user_dishes: ScheduledUserDishType[]; -} - -export type ScheduleDataType = { - user: UserType; - scheduled_user_dish: UserDishType | null; - user_dishes: UserDishType[]; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/ScheduledUserDishType.ts b/frontend-old/archive/src/types/ScheduledUserDishType.ts deleted file mode 100644 index 5ff7c94..0000000 --- a/frontend-old/archive/src/types/ScheduledUserDishType.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {DishType} from "@/types/DishType"; -import {RecurrenceType} from "@/types/ScheduleType"; - -export type UserDishType = { - id: number; - dish: DishType; - user: UserType; - recurrences: RecurrenceType[]; -} - -export type UserDishWithoutUserType = { - id: number; - dish: DishType; - recurrences: RecurrenceType[]; -} - -export type ScheduledUserDishType = { - id: number; - user_dish: UserDishType; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/UserDishType.ts b/frontend-old/archive/src/types/UserDishType.ts deleted file mode 100644 index 32f9dfc..0000000 --- a/frontend-old/archive/src/types/UserDishType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {RecurrenceType} from "@/types/ScheduleType"; - -export type DishType = { - user: UserType; - dish: DishType; - recurrences: RecurrenceType[]; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/UserType.ts b/frontend-old/archive/src/types/UserType.ts deleted file mode 100644 index 1129ff0..0000000 --- a/frontend-old/archive/src/types/UserType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {UserDishWithoutUserType} from "@/types/ScheduledUserDishType"; - -export type UserType = { - id: number; - name: string; - user_dishes: UserDishWithoutUserType[]; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/apiRequest.ts b/frontend-old/archive/src/utils/api/apiRequest.ts deleted file mode 100644 index e0690fa..0000000 --- a/frontend-old/archive/src/utils/api/apiRequest.ts +++ /dev/null @@ -1,107 +0,0 @@ -export const apiRequest = async (url: string, options: RequestInit = {}) => { - const token = localStorage.getItem('token'); - - const allowedRequests = [ - '/api/auth/login', - '/api/auth/register', - ] - - if (allowedRequests.includes(url)) { - return publicRequest(url, options) - } - - if (!token) { - throw new Error('No authentication token found.' + url); - } - - return privateRequest(url, token, options); -}; - -export const publicRequest = async (fullUrl: string, options: RequestInit = {}) => { - console.log('→ Sending request', fullUrl, options.method); - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { - headers: { - ...(options.headers || {}), - }, - ...options, - }); - - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - -export const privateRequest = async (fullUrl: string, token: string, options: RequestInit = {}) => { - const headers = { - ...(options.headers || {}), - Authorization: `Bearer ${token}`, - }; - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { headers, ...options }); - - // Authentication failure - token invalid - redirect to login - if (response.status === 401) { - localStorage.removeItem('token'); - localStorage.removeItem('refreshToken'); - - window.location.href = '/login'; - - throw new Error('Unauthorized. Redirecting to login.'); - } - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - - -// Add shorthand HTTP methods -apiRequest.get = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'GET' }); -}; - -apiRequest.post = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.put = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.delete = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'DELETE' }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/auth.ts b/frontend-old/archive/src/utils/api/auth.ts deleted file mode 100644 index f4511aa..0000000 --- a/frontend-old/archive/src/utils/api/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { apiRequest } from '@/utils/api/apiRequest'; - -export const login = async (email: string, password: string) => { - const data = await apiRequest.post('/api/auth/login', { email, password }); - - if (!data.access_token) { - throw new Error('No access token returned from login.'); - } - - localStorage.setItem('token', data.access_token); - - return data; -}; - - -export const register = async (name: string, email: string, password: string, passwordConfirmation: string) => { - const data = await apiRequest.post('/api/auth/register', { - name, - email, - password, - password_confirmation: passwordConfirmation, // Match the backend's expected parameter - }); - - // Store the token (if returned by the backend) similarly to login - localStorage.setItem('token', data.access_token); - - return data; -}; diff --git a/frontend-old/archive/src/utils/api/dishApi.ts b/frontend-old/archive/src/utils/api/dishApi.ts deleted file mode 100644 index 8d38dba..0000000 --- a/frontend-old/archive/src/utils/api/dishApi.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {apiRequest} from "@/utils/api/apiRequest"; - -export const listDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dishes) { - return data.payload.dishes as DishType[]; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const fetchDish = async (id: number): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dish) { - return data.payload.dish as DishType; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const createDish = async ( - name: string, - // recurrence: number, - // userIds: number[] -) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes`, { - name, - // recurrence, - // users: userIds, - }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch(() => { - throw new Error("Failed to create dish. Please try again later."); - }); -}; - -export const updateDish = async (dish_id: number, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.put(`/api/dishes/${dish_id}`, {name}, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .catch((error) => { - throw error; - }); -}; - -export const deleteDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.delete(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const addUserToDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/add`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const removeUserFromDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/remove`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/archive/src/utils/api/scheduleApi.ts b/frontend-old/archive/src/utils/api/scheduleApi.ts deleted file mode 100644 index d7fcbf3..0000000 --- a/frontend-old/archive/src/utils/api/scheduleApi.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { isValidDate } from "@/helpers/Date"; - -export const listSchedule = async (startDate?: string, endDate?: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (startDate && !isValidDate(startDate)) { - throw new Error('Invalid start date'); - } - if (endDate && !isValidDate(endDate)) { - throw new Error('Invalid end date'); - } - - const params = new URLSearchParams(); - if (startDate) params.append('start', startDate); - if (endDate) params.append('end', endDate); - - const endpoint = `/api/schedule${params.toString() ? `?${params.toString()}` : ''}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule); -}; - -export const getScheduleForDate = async (date: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Update the schedule for a specific date (e.g., mark as skipped) -export const updateScheduleForDate = async (date: string, isSkipped: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.put(endpoint, { is_skipped: isSkipped }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Generate a new schedule (optional: overwrite the existing one) -export const generateSchedule = async (overwrite: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const endpoint = `/api/schedule/generate`; - - return apiRequest.post(endpoint, { overwrite }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const scheduleUserDish = async (date: string, user_id: number, user_dish_id: number|null, skipped: boolean = false) => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - const endpoint = `/api/schedule/${date}/user-dishes`; - - return apiRequest.post(endpoint, { user_dish_id, user_id, skipped }, { headers: { Authorization: `Bearer ${token}`} }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { throw err }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts b/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts deleted file mode 100644 index 8a9b66c..0000000 --- a/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; - -export const listScheduledUserDishesStartingFromDate = async (startDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?start=${startDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const listScheduledUserDishesEndingAtDate = async (endDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?end=${endDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const getScheduledUserDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; - -export const updateScheduledUserDish = async (id: number, userDishId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const payload = userDishId > 0 - ? { user_dish_id: userDishId } - : { user_dish_id: null, is_skipped: true }; - - return apiRequest.put(`/api/scheduled-user-dishes/${id}`, payload, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/userDishApi.ts b/frontend-old/archive/src/utils/api/userDishApi.ts deleted file mode 100644 index 39ba27f..0000000 --- a/frontend-old/archive/src/utils/api/userDishApi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { UserDishType } from "@/types/ScheduledUserDishType"; - -export const listUserDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - return apiRequest.get(`/api/user-dishes`, { headers: { Authorization: `Bearer ${ token }` } }) - .then((data) => { - if (data?.payload?.user_dishes) return data.payload.user_dishes as UserDishType[]; - - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/archive/src/utils/api/usersApi.ts b/frontend-old/archive/src/utils/api/usersApi.ts deleted file mode 100644 index 8d70ed8..0000000 --- a/frontend-old/archive/src/utils/api/usersApi.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {RecurrenceType} from "@/types/ScheduleType"; -import {apiRequest} from "@/utils/api/apiRequest"; -import {UserType} from "@/types/UserType"; - -export const listUsers = async () => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.users) { - return data.payload.users; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const showUser = async (userId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users/${userId}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - if (data?.payload?.user) { - return data.payload.user; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const createUser = async (name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.post('/api/users', {name}, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const updateUser = async (user: UserType, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.put(`/api/users/${user.id}`, { name }, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const deleteUser = async (user: UserType) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.delete(`/api/users/${user.id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - - -export const getUserDishForUserAndDish = async (userId: number, dishId: number) => { - const endpoint = `/api/users/${userId}/dishes/${dishId}`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.user_dish) { - return data.payload.user_dish; - } - throw new Error('Failed to fetch user dish'); - }) - .catch((err) => { - throw err; - }); -}; - -export const syncUserDishRecurrences = async ( - dish_id: number, - user_id: number, - recurrenceData: RecurrenceType[] -) => { - const url = `/api/users/${user_id}/dishes/${dish_id}/recurrences`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(url, { recurrences: recurrenceData }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch((err) => { - throw err; - }); -}; - diff --git a/frontend-old/archive/src/utils/dateBuilder.ts b/frontend-old/archive/src/utils/dateBuilder.ts deleted file mode 100644 index e41ce15..0000000 --- a/frontend-old/archive/src/utils/dateBuilder.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DateTime } from "luxon"; - -const transformDate = (inputDate: string, addSuffix = false): string => { - const date = DateTime.fromISO(inputDate) - const day = date.day - const suffix = addSuffix ? getDaySuffix(day) : '' - return date.toFormat("MMMM") + ` ${ day }${ suffix }, ` + date.toFormat("yyyy"); -} - -const getDaySuffix = (day: number): string => { - if (day >= 11 && day <= 13) return "th"; - switch (day % 10) { - case 1: - return "st"; - case 2: - return "nd"; - case 3: - return "rd"; - default: - return "th"; - } -}; - -export default transformDate; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/scheduleBuilder.ts b/frontend-old/archive/src/utils/scheduleBuilder.ts deleted file mode 100644 index 8bbf15f..0000000 --- a/frontend-old/archive/src/utils/scheduleBuilder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ScheduleDataType, ScheduleType } from "@/types/ScheduleType"; -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -const ScheduleBuilder = ( - schedule: ScheduleType, - users: UserType[], - userDishes: UserDishType[] -): ScheduleDataType[] => users.map(user => { - return { - user, - scheduled_user_dish: schedule.scheduled_user_dishes - .filter((scheduledUserDish: ScheduledUserDishType) => scheduledUserDish.user_dish?.user.id === user.id) - .shift()?.user_dish ?? null, - user_dishes: userDishes.filter((userDish: UserDishType) => userDish.user.id === user.id) - } -}) - -export default ScheduleBuilder \ No newline at end of file diff --git a/frontend-old/archive/tailwind.config.ts b/frontend-old/archive/tailwind.config.ts deleted file mode 100644 index 109807b..0000000 --- a/frontend-old/archive/tailwind.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Config } from "tailwindcss"; - -export default { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - colors: { - background: "var(--background)", - foreground: "var(--foreground)", - }, - }, - }, - plugins: [], -} satisfies Config; diff --git a/frontend-old/archive/tsconfig.json b/frontend-old/archive/tsconfig.json deleted file mode 100644 index c133409..0000000 --- a/frontend-old/archive/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/frontend-old/package-lock.json b/frontend-old/package-lock.json deleted file mode 100644 index af085dc..0000000 --- a/frontend-old/package-lock.json +++ /dev/null @@ -1,5099 +0,0 @@ -{ - "name": "my-react-router-app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "my-react-router-app", - "dependencies": { - "@headlessui/react": "^2.2.9", - "@heroicons/react": "^2.2.0", - "@react-router/node": "^7.5.3", - "@react-router/serve": "^7.5.3", - "@types/luxon": "^3.7.1", - "classnames": "^2.5.1", - "isbot": "^5.1.27", - "luxon": "^3.7.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router": "^7.5.3" - }, - "devDependencies": { - "@react-router/dev": "^7.5.3", - "@tailwindcss/vite": "^4.1.6", - "@types/node": "^20", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "tailwindcss": "^4.1.6", - "typescript": "^5.8.3", - "vite": "^6.3.3", - "vite-tsconfig-paths": "^5.1.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", - "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helpers": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", - "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", - "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", - "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", - "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react": { - "version": "0.26.28", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", - "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.1.2", - "@floating-ui/utils": "^0.2.8", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@headlessui/react": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz", - "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/react": "^0.26.16", - "@react-aria/focus": "^3.20.2", - "@react-aria/interactions": "^3.25.0", - "@tanstack/react-virtual": "^3.13.9", - "use-sync-external-store": "^1.5.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "react-dom": "^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/@heroicons/react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", - "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", - "license": "MIT", - "peerDependencies": { - "react": ">= 16 || ^19.0.0-rc" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mjackson/node-fetch-server": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz", - "integrity": "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==", - "license": "MIT" - }, - "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@npmcli/package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^4.1.0", - "glob": "^10.2.2", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "proc-log": "^3.0.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", - "dev": true, - "license": "ISC", - "dependencies": { - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@react-aria/focus": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz", - "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/interactions": "^3.25.6", - "@react-aria/utils": "^3.31.0", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/interactions": { - "version": "3.25.6", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz", - "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/ssr": "^3.9.10", - "@react-aria/utils": "^3.31.0", - "@react-stately/flags": "^3.1.2", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/ssr": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", - "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/utils": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.31.0.tgz", - "integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/ssr": "^3.9.10", - "@react-stately/flags": "^3.1.2", - "@react-stately/utils": "^3.10.8", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-router/dev": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.6.0.tgz", - "integrity": "sha512-XSxEslex0ddJPxNNgdU1Eqmc9lsY/lhcLNCcRLAtlrOPyOz3Y8kIPpAf5T/U2AG3HGXFVBa9f8aQ7wXU3wTJSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.8", - "@babel/generator": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/plugin-syntax-decorators": "^7.22.10", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/preset-typescript": "^7.21.5", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.22.5", - "@npmcli/package-json": "^4.0.1", - "@react-router/node": "7.6.0", - "arg": "^5.0.1", - "babel-dead-code-elimination": "^1.0.6", - "chokidar": "^4.0.0", - "dedent": "^1.5.3", - "es-module-lexer": "^1.3.1", - "exit-hook": "2.2.1", - "fs-extra": "^10.0.0", - "jsesc": "3.0.2", - "lodash": "^4.17.21", - "pathe": "^1.1.2", - "picocolors": "^1.1.1", - "prettier": "^2.7.1", - "react-refresh": "^0.14.0", - "semver": "^7.3.7", - "set-cookie-parser": "^2.6.0", - "valibot": "^0.41.0", - "vite-node": "3.0.0-beta.2" - }, - "bin": { - "react-router": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@react-router/serve": "^7.6.0", - "react-router": "^7.6.0", - "typescript": "^5.1.0", - "vite": "^5.1.0 || ^6.0.0", - "wrangler": "^3.28.2 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@react-router/serve": { - "optional": true - }, - "typescript": { - "optional": true - }, - "wrangler": { - "optional": true - } - } - }, - "node_modules/@react-router/express": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.6.0.tgz", - "integrity": "sha512-nxSTCcTsVx94bXOI9JjG7Cg338myi8EdQWTOjA97v2ApX35wZm/ZDYos5MbrvZiMi0aB4KgAD62o4byNqF9Z1A==", - "license": "MIT", - "dependencies": { - "@react-router/node": "7.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "express": "^4.17.1 || ^5", - "react-router": "7.6.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/node": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.6.0.tgz", - "integrity": "sha512-agjDPUzisLdGJ7Q2lx/Z3OfdS2t1k6qv/nTvA45iahGsQJCMDvMqVoIi7iIULKQJwrn4HWjM9jqEp75+WsMOXg==", - "license": "MIT", - "dependencies": { - "@mjackson/node-fetch-server": "^0.2.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2", - "undici": "^6.19.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.6.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/serve": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.6.0.tgz", - "integrity": "sha512-2O8ALEYgJfimvEdNRqMpnZb2N+DQ5UK/SKo9Xo3mTkt3no0rNTcNxzmhzD2tm92Q/HI7kHmMY1nBegNB2i1abA==", - "license": "MIT", - "dependencies": { - "@react-router/express": "7.6.0", - "@react-router/node": "7.6.0", - "compression": "^1.7.4", - "express": "^4.19.2", - "get-port": "5.1.1", - "morgan": "^1.10.0", - "source-map-support": "^0.5.21" - }, - "bin": { - "react-router-serve": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.6.0" - } - }, - "node_modules/@react-stately/flags": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", - "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@react-stately/utils": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz", - "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/shared": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz", - "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", - "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", - "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", - "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", - "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", - "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", - "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", - "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", - "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", - "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", - "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", - "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", - "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", - "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", - "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", - "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", - "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", - "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", - "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", - "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", - "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz", - "integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.29.2", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.6" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz", - "integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-x64": "4.1.6", - "@tailwindcss/oxide-freebsd-x64": "4.1.6", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.6", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-x64-musl": "4.1.6", - "@tailwindcss/oxide-wasm32-wasi": "4.1.6", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.6" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz", - "integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz", - "integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz", - "integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz", - "integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz", - "integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz", - "integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz", - "integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz", - "integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz", - "integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz", - "integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.9", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz", - "integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz", - "integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.6.tgz", - "integrity": "sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.6", - "@tailwindcss/oxide": "4.1.6", - "tailwindcss": "4.1.6" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6" - } - }, - "node_modules/@tanstack/react-virtual": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", - "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", - "license": "MIT", - "dependencies": { - "@tanstack/virtual-core": "3.13.12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@tanstack/virtual-core": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", - "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.17.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.46.tgz", - "integrity": "sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/react": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", - "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.4.tgz", - "integrity": "sha512-WxYAszDYgsMV31OVyoG4jbAgJI1Gw0Xq9V19zwhy6+hUUJlJIdZ3r/cbdmTqFv++SktQkZ/X+46yGFxp5XJBEg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/babel-dead-code-elimination": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz", - "integrity": "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001717", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", - "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.0.2", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.152", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz", - "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/exit-hook": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz", - "integrity": "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isbot": { - "version": "5.1.28", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.28.tgz", - "integrity": "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw==", - "license": "Unlicense", - "engines": { - "node": ">=18" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lightningcss": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", - "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.2", - "lightningcss-darwin-x64": "1.29.2", - "lightningcss-freebsd-x64": "1.29.2", - "lightningcss-linux-arm-gnueabihf": "1.29.2", - "lightningcss-linux-arm64-gnu": "1.29.2", - "lightningcss-linux-arm64-musl": "1.29.2", - "lightningcss-linux-x64-gnu": "1.29.2", - "lightningcss-linux-x64-musl": "1.29.2", - "lightningcss-win32-arm64-msvc": "1.29.2", - "lightningcss-win32-x64-msvc": "1.29.2" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", - "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", - "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", - "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", - "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", - "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", - "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", - "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", - "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", - "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", - "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/luxon": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", - "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", - "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", - "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router/node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/rollup": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", - "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.2", - "@rollup/rollup-android-arm64": "4.40.2", - "@rollup/rollup-darwin-arm64": "4.40.2", - "@rollup/rollup-darwin-x64": "4.40.2", - "@rollup/rollup-freebsd-arm64": "4.40.2", - "@rollup/rollup-freebsd-x64": "4.40.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", - "@rollup/rollup-linux-arm-musleabihf": "4.40.2", - "@rollup/rollup-linux-arm64-gnu": "4.40.2", - "@rollup/rollup-linux-arm64-musl": "4.40.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-musl": "4.40.2", - "@rollup/rollup-linux-s390x-gnu": "4.40.2", - "@rollup/rollup-linux-x64-gnu": "4.40.2", - "@rollup/rollup-linux-x64-musl": "4.40.2", - "@rollup/rollup-win32-arm64-msvc": "4.40.2", - "@rollup/rollup-win32-ia32-msvc": "4.40.2", - "@rollup/rollup-win32-x64-msvc": "4.40.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-slice": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz", - "integrity": "sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "license": "MIT" - }, - "node_modules/tailwindcss": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz", - "integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsconfck": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz", - "integrity": "sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", - "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/valibot": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.41.0.tgz", - "integrity": "sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "typescript": ">=5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.0.0-beta.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.0-beta.2.tgz", - "integrity": "sha512-ofTf6cfRdL30Wbl9n/BX81EyIR5s4PReLmSurrxQ+koLaWUNOEo8E0lCM53OJkb8vpa2URM2nSrxZsIFyvY1rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/frontend-old/package.json b/frontend-old/package.json deleted file mode 100644 index cc47acc..0000000 --- a/frontend-old/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "my-react-router-app", - "private": true, - "type": "module", - "scripts": { - "build": "react-router build", - "dev": "react-router dev", - "start": "react-router-serve ./build/server/index.js", - "typecheck": "react-router typegen && tsc" - }, - "dependencies": { - "@headlessui/react": "^2.2.9", - "@heroicons/react": "^2.2.0", - "@react-router/node": "^7.5.3", - "@react-router/serve": "^7.5.3", - "@types/luxon": "^3.7.1", - "classnames": "^2.5.1", - "isbot": "^5.1.27", - "luxon": "^3.7.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router": "^7.5.3" - }, - "devDependencies": { - "@react-router/dev": "^7.5.3", - "@tailwindcss/vite": "^4.1.6", - "@types/node": "^20", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "tailwindcss": "^4.1.6", - "typescript": "^5.8.3", - "vite": "^6.3.3", - "vite-tsconfig-paths": "^5.1.4" - } -} diff --git a/frontend-old/public/favicon.ico b/frontend-old/public/favicon.ico deleted file mode 100644 index 5dbdfcd..0000000 Binary files a/frontend-old/public/favicon.ico and /dev/null differ diff --git a/frontend-old/react-router.config.ts b/frontend-old/react-router.config.ts deleted file mode 100644 index 6ff16f9..0000000 --- a/frontend-old/react-router.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Config } from "@react-router/dev/config"; - -export default { - // Config options... - // Server-side render by default, to enable SPA mode set this to `false` - ssr: true, -} satisfies Config; diff --git a/frontend-old/tsconfig.json b/frontend-old/tsconfig.json deleted file mode 100644 index cfe4682..0000000 --- a/frontend-old/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "include": [ - "**/*", - "**/.server/**/*", - "**/.client/**/*", - ".react-router/types/**/*" - ], - "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], - "baseUrl": ".", - "paths": { - "~/*": ["./app/*"], - "@/*": ["./app/*"] - }, - "esModuleInterop": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true - }, - "optimizeDeps": { - "include": ["react", "react-dom"] - } -} diff --git a/frontend-old/vite.config.ts b/frontend-old/vite.config.ts deleted file mode 100644 index 93a885d..0000000 --- a/frontend-old/vite.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { reactRouter } from "@react-router/dev/vite"; -import tailwindcss from "@tailwindcss/vite"; -import { defineConfig } from "vite"; -import tsconfigPaths from "vite-tsconfig-paths"; - -export default defineConfig({ - plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], - server: { - proxy: { - "/api": { - target: "http://localhost:8000", - changeOrigin: true, - }, - "/sanctum": { - target: "http://localhost:8000", - changeOrigin: true, - }, - }, - }, -}); diff --git a/resources/views/billing/index.blade.php b/resources/views/billing/index.blade.php deleted file mode 100644 index 00c8900..0000000 --- a/resources/views/billing/index.blade.php +++ /dev/null @@ -1,106 +0,0 @@ - -
-
-

BILLING

- - @if (session('success')) -
-

{{ session('success') }}

-
- @endif - - @if (session('error')) -
-

{{ session('error') }}

-
- @endif - -
-

Subscription Details

- -
-
- Plan - {{ $planType }} -
- -
- Status - - {{ ucfirst($subscription->stripe_status) }} - -
- - @if($nextBillingDate) -
- Next billing date - {{ $nextBillingDate->format('F j, Y') }} -
- @endif - - @if($subscription->ends_at) -
- Access until - {{ $subscription->ends_at->format('F j, Y') }} -
- @endif - - @if($planner->pm_last_four) -
- Payment method - - {{ ucfirst($planner->pm_type ?? 'Card') }} - •••• {{ $planner->pm_last_four }} - Update - -
- @endif -
- - @if(!$subscription->ends_at) -
- -
- @endif -
-
- - -
-
-

Cancel Subscription?

-

- Are you sure you want to cancel your subscription? You will retain access until the end of your current billing period. -

-
- -
- @csrf - -
-
-
-
-
-
diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index d893086..c007beb 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -65,11 +65,6 @@ class="inline-flex items-center px-3 py-2 text-sm font-medium {{ request()->rout x-transition class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-gray-700 ring-1 ring-secondary">
- @if(is_mode_saas()) - - Billing - - @endif
@csrf -
-
- @else -
-

Subscribe to Dish Planner

- -
-
- @csrf - -
-

Monthly

-

Billed monthly

- -
-
- -
- @csrf - -
-

Yearly

-

Billed annually

- -
-
-
-
- @endif - - - diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php deleted file mode 100644 index b9d609c..0000000 --- a/resources/views/welcome.blade.php +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - - Laravel - - - - - - - @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) - @vite(['resources/css/app.css', 'resources/js/app.js']) - @else - - @endif - - - - - diff --git a/routes/web.php b/routes/web.php index f23fbc5..c1e49da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,7 +3,6 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\Auth\RegisterController; -use App\Http\Controllers\SubscriptionController; Route::get('/', function () { return redirect()->route('dashboard'); @@ -26,25 +25,19 @@ Route::middleware('auth')->group(function () { Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); - // Routes requiring active subscription in SaaS mode - Route::middleware('subscription')->group(function () { - Route::get('/dashboard', function () { - return view('dashboard'); - })->name('dashboard'); + Route::get('/dashboard', function () { + return view('dashboard'); + })->name('dashboard'); - Route::get('/dishes', function () { - return view('dishes.index'); - })->name('dishes.index'); + Route::get('/dishes', function () { + return view('dishes.index'); + })->name('dishes.index'); - Route::get('/schedule', function () { - return view('schedule.index'); - })->name('schedule.index'); + Route::get('/schedule', function () { + return view('schedule.index'); + })->name('schedule.index'); - Route::get('/users', function () { - return view('users.index'); - })->name('users.index'); - - Route::get('/billing', [SubscriptionController::class, 'billing'])->name('billing')->middleware('saas'); - Route::get('/billing/portal', [SubscriptionController::class, 'billingPortal'])->name('billing.portal')->middleware('saas'); - }); + Route::get('/users', function () { + return view('users.index'); + })->name('users.index'); }); diff --git a/routes/web/subscription.php b/routes/web/subscription.php deleted file mode 100644 index d3258b9..0000000 --- a/routes/web/subscription.php +++ /dev/null @@ -1,18 +0,0 @@ -name('cashier.webhook'); - -Route::middleware('auth')->group(function () { - Route::get('/subscription', function () { - return view('subscription.index'); - })->name('subscription.index'); - - Route::post('/subscription/checkout', [SubscriptionController::class, 'checkout'])->name('subscription.checkout'); - Route::get('/subscription/success', [SubscriptionController::class, 'success'])->name('subscription.success'); - Route::post('/subscription/cancel', [SubscriptionController::class, 'cancel'])->name('subscription.cancel'); -}); diff --git a/shell.nix b/shell.nix index 4ff3195..d4b44be 100644 --- a/shell.nix +++ b/shell.nix @@ -85,24 +85,24 @@ pkgs.mkShell { prod-build() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" + local REGISTRY="forge.lvl0.xyz" local NAMESPACE="lvl0" - local IMAGE_NAME="dish-planner" + local IMAGE_NAME="dishplanner" echo "🔨 Building production image..." - podman build -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . + podman build --format docker -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . echo "✅ Build complete: ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" - echo "Run 'prod-push' to push to Codeberg" + echo "Run 'prod-push' to push to Forgejo" } prod-push() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" + local REGISTRY="forge.lvl0.xyz" local NAMESPACE="lvl0" - local IMAGE_NAME="dish-planner" + local IMAGE_NAME="dishplanner" - echo "📤 Pushing to Codeberg registry..." + echo "📤 Pushing to Forgejo registry..." if podman push ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}; then echo "✅ Image pushed to ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" else @@ -114,15 +114,15 @@ pkgs.mkShell { prod-build-nc() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" + local REGISTRY="forge.lvl0.xyz" local NAMESPACE="lvl0" - local IMAGE_NAME="dish-planner" + local IMAGE_NAME="dishplanner" echo "🔨 Building production image (no cache)..." - podman build --no-cache -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . + podman build --format docker --no-cache -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . echo "✅ Build complete: ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" - echo "Run 'prod-push' to push to Codeberg" + echo "Run 'prod-push' to push to Forgejo" } prod-build-push() { @@ -131,8 +131,8 @@ pkgs.mkShell { } prod-login() { - echo "📝 Logging into Codeberg registry..." - podman login codeberg.org + echo "📝 Logging into Forgejo registry..." + podman login forge.lvl0.xyz } echo "🚀 Dish Planner Development Environment" @@ -154,9 +154,9 @@ pkgs.mkShell { echo " dev-fix-permissions - Fix Docker-created file permissions" echo "" echo "Production commands:" - echo " prod-login - Login to Codeberg registry" + echo " prod-login - Login to Forgejo registry" echo " prod-build [tag] - Build production image (default: latest)" - echo " prod-push [tag] - Push image to Codeberg" + echo " prod-push [tag] - Push image to Forgejo" echo " prod-build-push - Build and push in one command" echo "" diff --git a/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayAction.php b/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayAction.php index e4416fe..85f0e8f 100644 --- a/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayAction.php +++ b/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayAction.php @@ -10,9 +10,9 @@ class RegenerateScheduleDayAction { public function execute(Planner $planner, Schedule $schedule, bool $overwrite = false): void { - User::all() - ->each(fn (User $user) => resolve(RegenerateScheduleDayForUserAction::class) - ->execute($planner, $schedule, $user, $overwrite) - ); + /** @var RegenerateScheduleDayForUserAction $action */ + $action = resolve(RegenerateScheduleDayForUserAction::class); + + $planner->users->each(fn (User $user) => $action->execute($planner, $schedule, $user, $overwrite)); } } diff --git a/update.sh b/update.sh deleted file mode 100755 index 7320639..0000000 --- a/update.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e - -echo "🔄 Pulling latest backend changes..." -git pull origin main - -echo "📦 Installing PHP dependencies..." -composer install --no-interaction --prefer-dist --optimize-autoloader - -echo "🗄️ Running migrations..." -php artisan migrate --force - -echo "🧹 Clearing and caching config..." -php artisan config:cache -php artisan route:cache -php artisan view:cache - -echo "✅ Backend update complete!"