From 91b3ac7457c5e8d1ac31e54da04328bc32e24347 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Thu, 18 Dec 2025 12:43:02 -0500 Subject: [PATCH 01/17] Order model and migration created --- backend/app/models/order.rb | 8 ++++++++ backend/db/migrate/20251218161810_create_orders.rb | 12 ++++++++++++ backend/db/schema.rb | 10 +++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 backend/app/models/order.rb create mode 100644 backend/db/migrate/20251218161810_create_orders.rb diff --git a/backend/app/models/order.rb b/backend/app/models/order.rb new file mode 100644 index 0000000..e7726c6 --- /dev/null +++ b/backend/app/models/order.rb @@ -0,0 +1,8 @@ +class Order < ApplicationRecord + enum status: { + pending: 0, + processing: 1, + completed: 2, + cancelled: 3 + } +end diff --git a/backend/db/migrate/20251218161810_create_orders.rb b/backend/db/migrate/20251218161810_create_orders.rb new file mode 100644 index 0000000..b450f66 --- /dev/null +++ b/backend/db/migrate/20251218161810_create_orders.rb @@ -0,0 +1,12 @@ +class CreateOrders < ActiveRecord::Migration[7.2] + def change + create_table :orders do |t| + t.string :title + t.text :description + t.date :due + t.integer :status, default: 0, null: false + + t.timestamps + end + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index 83701a2..58ef39d 100644 --- a/backend/db/schema.rb +++ b/backend/db/schema.rb @@ -10,8 +10,16 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 0) do +ActiveRecord::Schema[7.2].define(version: 2025_12_18_161810) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "orders", force: :cascade do |t| + t.string "title" + t.text "description" + t.date "due" + t.integer "status", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end end From 96303ff7cb2e646b98c697daac55f5454d21fa0f Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Thu, 18 Dec 2025 13:06:12 -0500 Subject: [PATCH 02/17] Order CRUD and errors handling created --- .../app/controllers/application_controller.rb | 18 +++++++++++ backend/app/controllers/orders_controller.rb | 31 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 backend/app/controllers/orders_controller.rb diff --git a/backend/app/controllers/application_controller.rb b/backend/app/controllers/application_controller.rb index 4ac8823..48b3c1a 100644 --- a/backend/app/controllers/application_controller.rb +++ b/backend/app/controllers/application_controller.rb @@ -1,2 +1,20 @@ class ApplicationController < ActionController::API + rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found + rescue_from ActiveRecord::RecordInvalid, with: :handle_invalid_record + rescue_from StandardError, with: :handle_other_errors + + private + + def handle_not_found + render json: {error: "Record not found"}, status: :not_found + end + + def handle_invalid_record(e) + render json: {errors: e.record.errors}, status: :unprocessable_entity + end + + def handle_other_errors(e) + render json: {errors: e.message}, status: :internal_server_error + end + end diff --git a/backend/app/controllers/orders_controller.rb b/backend/app/controllers/orders_controller.rb new file mode 100644 index 0000000..e22fcfe --- /dev/null +++ b/backend/app/controllers/orders_controller.rb @@ -0,0 +1,31 @@ +class OrdersController < ApplicationController + def index + orders = Order.all.order(created_at: :desc) + render json: orders, status: :ok + end + + def create + new_order = Order.new(order_params) + new_order.save! + render json: new_order, status: :created + end + + def destroy + order_to_delete = Order.find(params[:id]) + order_to_delete.destroy! + render status: :ok + end + + def update + order_to_change = Order.find(params[:id]) + order_to_change.update!(order_params) + render json: order_to_change, status: :ok + end + + private + + def order_params + params.require(:order).permit(:title, :description, :due, :status) + end + +end From 8abb862df2e29c7297dbc580b3dec77596a49289 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Thu, 18 Dec 2025 13:18:39 -0500 Subject: [PATCH 03/17] refactor enum bc deprecation warning and validation of title --- backend/app/models/order.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/app/models/order.rb b/backend/app/models/order.rb index e7726c6..9bbcc0c 100644 --- a/backend/app/models/order.rb +++ b/backend/app/models/order.rb @@ -1,8 +1,4 @@ class Order < ApplicationRecord - enum status: { - pending: 0, - processing: 1, - completed: 2, - cancelled: 3 - } + validates :title, presence: true + enum :status, {:pending=>0, :processing=>1, :completed=>2, :cancelled=>3} end From e8b2ffe52adb205fd1feacd4f75843157e9934d1 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Thu, 18 Dec 2025 13:18:47 -0500 Subject: [PATCH 04/17] added routes --- backend/config/routes.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/config/routes.rb b/backend/config/routes.rb index 33c9639..25f666e 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -11,4 +11,6 @@ # Defines the root path route ("/") # root "posts#index" + + resources :orders end From 1ec7eaba83b156d897374a0f8b522b61755f79e3 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Thu, 18 Dec 2025 13:19:08 -0500 Subject: [PATCH 05/17] added controller and model tests --- .../controllers/orders_controller_test.rb | 45 +++++++++++++++++++ backend/test/fixtures/orders.yml | 13 ++++++ backend/test/models/order_test.rb | 16 +++++++ backend/test/test_helper.rb | 13 ++++++ 4 files changed, 87 insertions(+) create mode 100644 backend/test/controllers/orders_controller_test.rb create mode 100644 backend/test/fixtures/orders.yml create mode 100644 backend/test/models/order_test.rb create mode 100644 backend/test/test_helper.rb diff --git a/backend/test/controllers/orders_controller_test.rb b/backend/test/controllers/orders_controller_test.rb new file mode 100644 index 0000000..fb0ac97 --- /dev/null +++ b/backend/test/controllers/orders_controller_test.rb @@ -0,0 +1,45 @@ +require "test_helper" + +class OrdersControllerTest < ActionDispatch::IntegrationTest + fixtures :orders + + test "should get index" do + get orders_url + assert_response :success + + json_response = JSON.parse(response.body) + assert_equal 2, json_response.length + end + + test "should get index in order created_at desc" do + get orders_url + assert_response :success + + json_response = JSON.parse(response.body) + timestamps = json_response.map { |t| t["created_at"]} + assert_equal timestamps.sort.reverse, timestamps + end + + test "should create order" do + assert_difference("Order.count", 1) do + post orders_url, params: {order:{title:"A new order", status: "pending"}} + end + assert_response :created + end + + test "should delete order" do + assert_difference("Order.count", -1) do + delete order_url(1) + end + assert_response :success + end + + test "should update order" do + patch order_url(2), params: {order:{title:"Updated Title"}} + assert_response :success + + json_response = JSON.parse(response.body) + assert_equal "Updated Title", json_response["title"] + end + +end \ No newline at end of file diff --git a/backend/test/fixtures/orders.yml b/backend/test/fixtures/orders.yml new file mode 100644 index 0000000..cac35d3 --- /dev/null +++ b/backend/test/fixtures/orders.yml @@ -0,0 +1,13 @@ +one: + id: 1 + title: Order Number 1 + description: This is the first Order + due: 2026-12-12 + status: 3 + +two: + id: 2 + title: Order Number 2 + description: This is the second Order + due: 2026-12-12 + status: 0 \ No newline at end of file diff --git a/backend/test/models/order_test.rb b/backend/test/models/order_test.rb new file mode 100644 index 0000000..ec76a8b --- /dev/null +++ b/backend/test/models/order_test.rb @@ -0,0 +1,16 @@ +require "test_helper" + +class OrderTest < ActiveSupport::TestCase + test "should not create order without title" do + order = Order.new(description: "a description") + assert_not order.save + end + + test "should save order with default status pending" do + order = Order.new({title: "A new order", description: "a description"}) + assert order.save + assert_equal "pending", order.status + end +end + + \ No newline at end of file diff --git a/backend/test/test_helper.rb b/backend/test/test_helper.rb new file mode 100644 index 0000000..746c65f --- /dev/null +++ b/backend/test/test_helper.rb @@ -0,0 +1,13 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +class ActiveSupport::TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml + fixtures :all + + # Add more helper methods to be used by all tests here... +end \ No newline at end of file From d89b97be4f61ce685b29435d9718a90894f80988 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Fri, 19 Dec 2025 11:02:20 -0500 Subject: [PATCH 06/17] Added client field to order --- backend/db/migrate/20251219160000_add_client_to_orders.rb | 5 +++++ backend/db/schema.rb | 3 ++- backend/test/fixtures/orders.yml | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 backend/db/migrate/20251219160000_add_client_to_orders.rb diff --git a/backend/db/migrate/20251219160000_add_client_to_orders.rb b/backend/db/migrate/20251219160000_add_client_to_orders.rb new file mode 100644 index 0000000..2e4ed38 --- /dev/null +++ b/backend/db/migrate/20251219160000_add_client_to_orders.rb @@ -0,0 +1,5 @@ +class AddClientToOrders < ActiveRecord::Migration[7.2] + def change + add_column :orders, :client, :string + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index 58ef39d..ba92c40 100644 --- a/backend/db/schema.rb +++ b/backend/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_12_18_161810) do +ActiveRecord::Schema[7.2].define(version: 2025_12_19_160000) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -21,5 +21,6 @@ t.integer "status", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "client" end end diff --git a/backend/test/fixtures/orders.yml b/backend/test/fixtures/orders.yml index cac35d3..84b4814 100644 --- a/backend/test/fixtures/orders.yml +++ b/backend/test/fixtures/orders.yml @@ -1,6 +1,7 @@ one: id: 1 title: Order Number 1 + client: Client A description: This is the first Order due: 2026-12-12 status: 3 @@ -8,6 +9,7 @@ one: two: id: 2 title: Order Number 2 + client: Client B description: This is the second Order due: 2026-12-12 status: 0 \ No newline at end of file From 583960871e95db8f939b6943f65e4b76e03ff804 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Fri, 19 Dec 2025 11:02:34 -0500 Subject: [PATCH 07/17] Uncomment CORS config --- backend/config/initializers/cors.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/config/initializers/cors.rb b/backend/config/initializers/cors.rb index 0c5dd99..9fbee22 100644 --- a/backend/config/initializers/cors.rb +++ b/backend/config/initializers/cors.rb @@ -5,12 +5,12 @@ # Read more: https://github.com/cyu/rack-cors -# Rails.application.config.middleware.insert_before 0, Rack::Cors do -# allow do -# origins "example.com" -# -# resource "*", -# headers: :any, -# methods: [:get, :post, :put, :patch, :delete, :options, :head] -# end -# end +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins "*" + + resource "*", + headers: :any, + methods: [:get, :post, :put, :patch, :delete, :options, :head] + end +end From 3feee7191982820b09eee977be5565370880ab66 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Fri, 19 Dec 2025 11:05:35 -0500 Subject: [PATCH 08/17] FE: Orders table functional --- backend/Gemfile.lock | 3 + frontend/index.html | 2 +- frontend/package-lock.json | 730 ++++++++++++++++++- frontend/package.json | 3 + frontend/src/App.jsx | 29 +- frontend/src/components/OrdersDashboard.jsx | 3 + frontend/src/components/OrdersTable.jsx | 41 ++ frontend/src/components/forms/OrderForm.jsx | 7 + frontend/src/components/pages/OrdersPage.jsx | 38 + frontend/src/hooks/useFetchOrders.js | 20 + frontend/src/index.css | 1 - 11 files changed, 821 insertions(+), 56 deletions(-) create mode 100644 frontend/src/components/OrdersDashboard.jsx create mode 100644 frontend/src/components/OrdersTable.jsx create mode 100644 frontend/src/components/forms/OrderForm.jsx create mode 100644 frontend/src/components/pages/OrdersPage.jsx create mode 100644 frontend/src/hooks/useFetchOrders.js diff --git a/backend/Gemfile.lock b/backend/Gemfile.lock index 96570fa..789d4dc 100644 --- a/backend/Gemfile.lock +++ b/backend/Gemfile.lock @@ -127,6 +127,8 @@ GEM nio4r (2.7.4) nokogiri (1.18.8-aarch64-linux-gnu) racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-gnu) + racc (~> 1.4) parallel (1.27.0) parser (3.3.8.0) ast (~> 2.4.1) @@ -239,6 +241,7 @@ GEM PLATFORMS aarch64-linux + x86_64-linux DEPENDENCIES bootsnap diff --git a/frontend/index.html b/frontend/index.html index 0c589ec..e5bef3e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - Vite + React + Orders app
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 522f5f8..d067b59 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,9 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^7.3.6", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -41,7 +44,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", @@ -97,7 +99,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -131,7 +132,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", @@ -173,7 +173,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -183,7 +182,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -217,7 +215,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.0" @@ -261,11 +258,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", @@ -280,7 +285,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", @@ -299,7 +303,6 @@ "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" @@ -309,7 +312,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -319,6 +321,158 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", @@ -965,7 +1119,6 @@ "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", @@ -980,7 +1133,6 @@ "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" @@ -990,7 +1142,6 @@ "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" @@ -1000,20 +1151,235 @@ "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/@mui/core-downloads-tracker": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz", + "integrity": "sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz", + "integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/core-downloads-tracker": "^7.3.6", + "@mui/system": "^7.3.6", + "@mui/types": "^7.4.9", + "@mui/utils": "^7.3.6", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.6", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.6.tgz", + "integrity": "sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.6.tgz", + "integrity": "sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.6.tgz", + "integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/private-theming": "^7.3.6", + "@mui/styled-engine": "^7.3.6", + "@mui/types": "^7.4.9", + "@mui/utils": "^7.3.6", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.9.tgz", + "integrity": "sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.6.tgz", + "integrity": "sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/types": "^7.4.9", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.40.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", @@ -1353,11 +1719,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1373,6 +1750,15 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", @@ -1456,6 +1842,21 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1511,7 +1912,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1555,6 +1955,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "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", @@ -1589,6 +1998,31 @@ "dev": true, "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1608,14 +2042,12 @@ "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" @@ -1636,6 +2068,16 @@ "dev": true, "license": "MIT" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.140", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz", @@ -1643,6 +2085,15 @@ "dev": true, "license": "ISC" }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/esbuild": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", @@ -1698,7 +2149,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1934,6 +2384,12 @@ "node": ">=16.0.0" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1987,6 +2443,15 @@ "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", @@ -2033,6 +2498,33 @@ "node": ">=8" } }, + "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/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2047,7 +2539,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -2070,6 +2561,27 @@ "node": ">=0.8.19" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2104,7 +2616,6 @@ "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/js-yaml": { @@ -2124,7 +2635,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -2140,6 +2650,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2191,6 +2707,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2214,6 +2736,18 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2241,7 +2775,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -2277,6 +2810,15 @@ "dev": true, "license": "MIT" }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2331,7 +2873,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -2340,6 +2881,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2360,11 +2919,25 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "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": { @@ -2419,6 +2992,23 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2450,6 +3040,12 @@ "react": "^19.1.0" } }, + "node_modules/react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -2460,11 +3056,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -2549,6 +3180,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "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", @@ -2572,6 +3212,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2585,6 +3231,18 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tinyglobby": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", @@ -2764,6 +3422,24 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2c58f28..775a2eb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^7.3.6", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f67355a..45c9827 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,34 +1,9 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' import './App.css' +import OrdersPage from './components/pages/OrdersPage' function App() { - const [count, setCount] = useState(0) - return ( - <> - -

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- + ) } diff --git a/frontend/src/components/OrdersDashboard.jsx b/frontend/src/components/OrdersDashboard.jsx new file mode 100644 index 0000000..f19f530 --- /dev/null +++ b/frontend/src/components/OrdersDashboard.jsx @@ -0,0 +1,3 @@ +const OrdersDashboard = () => {} + +export default OrdersDashboard; \ No newline at end of file diff --git a/frontend/src/components/OrdersTable.jsx b/frontend/src/components/OrdersTable.jsx new file mode 100644 index 0000000..21f35dc --- /dev/null +++ b/frontend/src/components/OrdersTable.jsx @@ -0,0 +1,41 @@ +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; + +const COLUMNS = ['Id', 'Title', 'Client', 'Description', 'Due date', 'Status']; + +const OrdersTable = ({ orders }) => { + return ( + + + + + { + COLUMNS.map((column) => ( + {column} + )) + } + + + + {orders.map((order) => ( + + {order.id} + {order.title} + {order.client} + {order.description} + {order.due} + {order.status} + + ))} + +
+
+ ) +} + +export default OrdersTable; \ No newline at end of file diff --git a/frontend/src/components/forms/OrderForm.jsx b/frontend/src/components/forms/OrderForm.jsx new file mode 100644 index 0000000..dd62c3c --- /dev/null +++ b/frontend/src/components/forms/OrderForm.jsx @@ -0,0 +1,7 @@ +import * as React from 'react'; +import Modal from '@mui/material/Modal'; + +const OrderForm = () => { +} + +export default OrderForm; \ No newline at end of file diff --git a/frontend/src/components/pages/OrdersPage.jsx b/frontend/src/components/pages/OrdersPage.jsx new file mode 100644 index 0000000..4f9303e --- /dev/null +++ b/frontend/src/components/pages/OrdersPage.jsx @@ -0,0 +1,38 @@ +import { useEffect, useState } from 'react'; +import useFetchOrders from "../../hooks/useFetchOrders" +import Typography from "@mui/material/Typography"; +import OrdersDashboard from '../OrdersDashboard'; +import OrdersTable from '../OrdersTable'; +import OrderForm from '../forms/OrderForm'; + +const OrdersPage = () => { + + const [orders, setOrders] = useState([]); + const { fetchOrders } = useFetchOrders(); + + useEffect(()=>{ + const fetchData = async () => { + const obtainedOrders = await fetchOrders(); + if (obtainedOrders.error){ + alert(obtainedOrders.error); + return; + } else { + setOrders(obtainedOrders); + } + }; + fetchData(); + }, []); + + return ( + <> + + Orders + + + + + + ) +} + +export default OrdersPage; \ No newline at end of file diff --git a/frontend/src/hooks/useFetchOrders.js b/frontend/src/hooks/useFetchOrders.js new file mode 100644 index 0000000..02e4692 --- /dev/null +++ b/frontend/src/hooks/useFetchOrders.js @@ -0,0 +1,20 @@ +const useFetchOrders = () => { + const get_url = "http://localhost:3000/orders" + + const fetchOrders = async () => { + try { + const res = await fetch(get_url); + if (!res.ok) { + throw new Error('Error fetching tasks'); + } + const data = await res.json(); + return data; + } catch (error) { + return { error: error.message } + } + }; + + return { fetchOrders }; +} + +export default useFetchOrders; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 08a3ac9..0ca03a0 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -25,7 +25,6 @@ a:hover { body { margin: 0; display: flex; - place-items: center; min-width: 320px; min-height: 100vh; } From a01688d8821fd3d6d05963421c7ee6b5d677696e Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Sun, 21 Dec 2025 15:52:55 -0500 Subject: [PATCH 09/17] FE: New Order form functional --- frontend/package-lock.json | 111 +++++++++++++++ frontend/package.json | 2 + .../src/components/forms/OrderFormModal.jsx | 128 ++++++++++++++++++ frontend/src/components/pages/OrdersPage.jsx | 41 ++++-- frontend/src/hooks/useCreateOrder.js | 28 ++++ 5 files changed, 296 insertions(+), 14 deletions(-) create mode 100644 frontend/src/components/forms/OrderFormModal.jsx create mode 100644 frontend/src/hooks/useCreateOrder.js diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d067b59..f1cd092 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,8 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.6", + "@mui/x-date-pickers": "^8.22.1", + "dayjs": "^1.11.19", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -1370,6 +1372,94 @@ } } }, + "node_modules/@mui/x-date-pickers": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.22.1.tgz", + "integrity": "sha512-7tnUaKLllBYaliGeSP9vpN2Og33LYRfQW2Eq/F4xeNMQDUAa/6H2N822KW5NtdvW32KxjKkJiWZokgfBfVBtxg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "@mui/x-internals": "8.22.0", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.22.0.tgz", + "integrity": "sha512-PA7jCKRLbS6aYvTSbGr3Id4CPUdTrUejHm31l8Vje7dw138gBBHrHeGsqWJh/S5foorpK8loiRejKrLlTZokyQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -2044,6 +2134,12 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3072,6 +3168,12 @@ "react-dom": ">=16.6.0" } }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -3314,6 +3416,15 @@ "punycode": "^2.1.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/vite": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 775a2eb..5e6bbf2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,8 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.6", + "@mui/x-date-pickers": "^8.22.1", + "dayjs": "^1.11.19", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/frontend/src/components/forms/OrderFormModal.jsx b/frontend/src/components/forms/OrderFormModal.jsx new file mode 100644 index 0000000..3fab49a --- /dev/null +++ b/frontend/src/components/forms/OrderFormModal.jsx @@ -0,0 +1,128 @@ +import { useState, useEffect } from 'react'; +import Modal from '@mui/material/Modal'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import TextField from '@mui/material/TextField'; +import dayjs from 'dayjs'; +import useCreateOrder from '../../hooks/useCreateOrder'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import Typography from "@mui/material/Typography"; + +const style = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: 400, + bgcolor: 'background.paper', + borderRadius: '2%', + boxShadow: 10, + p: 4, + display: 'flex', + flexDirection: 'column', + gap: '12px', +}; + + +const OrderFormModal = ({ showModal, onClose, onOrderCreated }) => { + + const orderInitialValues = { + title: '', + client: '', + description: '', + dueDate: dayjs() + } + + const [formData, setFormData] = useState(orderInitialValues); + const [disabled, setDisabled] = useState(true); + const [loading, setLoading] = useState(false); + const { createOrder } = useCreateOrder(); + + useEffect(() => { + setDisabled(formData.title.trim() === ''); + }, [formData.title]); + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleDateChange = (newValue) => { + setFormData(prev => ({ + ...prev, + dueDate: newValue + })); + }; + + const onCreateOrder = async () => { + setLoading(true); + const creationResult = await createOrder(formData); + if (creationResult.error) { + alert(creationResult.error); + setLoading(false); + } else { + setLoading(false); + onClose(); + setFormData(orderInitialValues); + if (onOrderCreated) { + await onOrderCreated(); + } + } + }; + + return ( + + + + Create Order + + + + + + + + + + + ) +} + +export default OrderFormModal; \ No newline at end of file diff --git a/frontend/src/components/pages/OrdersPage.jsx b/frontend/src/components/pages/OrdersPage.jsx index 4f9303e..27b95b5 100644 --- a/frontend/src/components/pages/OrdersPage.jsx +++ b/frontend/src/components/pages/OrdersPage.jsx @@ -1,26 +1,30 @@ import { useEffect, useState } from 'react'; import useFetchOrders from "../../hooks/useFetchOrders" -import Typography from "@mui/material/Typography"; import OrdersDashboard from '../OrdersDashboard'; import OrdersTable from '../OrdersTable'; -import OrderForm from '../forms/OrderForm'; +import OrderFormModal from '../forms/OrderFormModal'; +import Typography from "@mui/material/Typography"; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; const OrdersPage = () => { const [orders, setOrders] = useState([]); const { fetchOrders } = useFetchOrders(); + const [showModal, setShowModal] = useState(false); + + const loadOrders = async () => { + const obtainedOrders = await fetchOrders(); + if (obtainedOrders.error) { + alert(obtainedOrders.error); + return; + } else { + setOrders(obtainedOrders); + } + }; - useEffect(()=>{ - const fetchData = async () => { - const obtainedOrders = await fetchOrders(); - if (obtainedOrders.error){ - alert(obtainedOrders.error); - return; - } else { - setOrders(obtainedOrders); - } - }; - fetchData(); + useEffect(() => { + loadOrders(); }, []); return ( @@ -29,8 +33,17 @@ const OrdersPage = () => { Orders + + + - + setShowModal(false)} + showModal={showModal} + onOrderCreated={loadOrders} + /> ) } diff --git a/frontend/src/hooks/useCreateOrder.js b/frontend/src/hooks/useCreateOrder.js new file mode 100644 index 0000000..e20fd93 --- /dev/null +++ b/frontend/src/hooks/useCreateOrder.js @@ -0,0 +1,28 @@ +const useCreateOrder = () => { + const api_url = "http://localhost:3000/orders" + + const createOrder = async (order) => { + try { + + const result = await fetch(api_url, { + method: 'POST', + headers: { + 'Content-type': 'application/json' + }, + body: JSON.stringify({ order }) + }) + const data = result.json(); + + if (!result.ok) { + throw new Error(data.errors?.join(', ') || 'Error creating order'); + } + return data; + } catch (error) { + return ({ error: error.message }) + } + } + + return { createOrder }; +}; + +export default useCreateOrder; \ No newline at end of file From 30b992fa7d71fdf3b431fc386f113f809464752c Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Sun, 21 Dec 2025 17:12:04 -0500 Subject: [PATCH 10/17] Fix in client and due date for order creation --- backend/app/controllers/orders_controller.rb | 2 +- frontend/src/components/forms/OrderForm.jsx | 7 ------- frontend/src/components/forms/OrderFormModal.jsx | 6 +++--- 3 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 frontend/src/components/forms/OrderForm.jsx diff --git a/backend/app/controllers/orders_controller.rb b/backend/app/controllers/orders_controller.rb index e22fcfe..3493f40 100644 --- a/backend/app/controllers/orders_controller.rb +++ b/backend/app/controllers/orders_controller.rb @@ -25,7 +25,7 @@ def update private def order_params - params.require(:order).permit(:title, :description, :due, :status) + params.require(:order).permit(:title, :description, :due, :status, :client) end end diff --git a/frontend/src/components/forms/OrderForm.jsx b/frontend/src/components/forms/OrderForm.jsx deleted file mode 100644 index dd62c3c..0000000 --- a/frontend/src/components/forms/OrderForm.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; -import Modal from '@mui/material/Modal'; - -const OrderForm = () => { -} - -export default OrderForm; \ No newline at end of file diff --git a/frontend/src/components/forms/OrderFormModal.jsx b/frontend/src/components/forms/OrderFormModal.jsx index 3fab49a..b9f6eb2 100644 --- a/frontend/src/components/forms/OrderFormModal.jsx +++ b/frontend/src/components/forms/OrderFormModal.jsx @@ -32,7 +32,7 @@ const OrderFormModal = ({ showModal, onClose, onOrderCreated }) => { title: '', client: '', description: '', - dueDate: dayjs() + due: dayjs() } const [formData, setFormData] = useState(orderInitialValues); @@ -55,7 +55,7 @@ const OrderFormModal = ({ showModal, onClose, onOrderCreated }) => { const handleDateChange = (newValue) => { setFormData(prev => ({ ...prev, - dueDate: newValue + due: newValue })); }; @@ -112,7 +112,7 @@ const OrderFormModal = ({ showModal, onClose, onOrderCreated }) => { From 97b87722090dcd275e63759a39bb62bacc5b7129 Mon Sep 17 00:00:00 2001 From: Lina Soler Date: Sun, 21 Dec 2025 17:12:30 -0500 Subject: [PATCH 11/17] FE: Orders Dashboard functional --- frontend/package-lock.json | 280 +++++++++++++++++++ frontend/package.json | 1 + frontend/src/components/OrdersDashboard.jsx | 119 +++++++- frontend/src/components/pages/OrdersPage.jsx | 2 +- 4 files changed, 399 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f1cd092..e56dfc0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.6", + "@mui/x-charts": "^8.22.1", "@mui/x-date-pickers": "^8.22.1", "dayjs": "^1.11.19", "react": "^19.0.0", @@ -1372,6 +1373,68 @@ } } }, + "node_modules/@mui/x-charts": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.22.1.tgz", + "integrity": "sha512-GHnBCRLXCYH6pjR2mI5H55FTuMGg6N630pOJ7aR5186zsHIVDN1EwnY1wWUn+vfen+4JiSNO04wUp8zWHbNwRw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "@mui/x-charts-vendor": "8.22.0", + "@mui/x-internal-gestures": "0.3.6", + "@mui/x-internals": "8.22.0", + "bezier-easing": "^2.1.0", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-charts-vendor": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.22.0.tgz", + "integrity": "sha512-lRG79nqZMeYfrXftcb+6Jd/A+GUNevv5AyCu2yi+YJJvjjPpBSy6rpEAS5/u3YcJpO08cL7Px1x6BY24w/x9jA==", + "license": "MIT AND ISC", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@types/d3-color": "^3.1.3", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-path": "^3.1.1", + "@types/d3-sankey": "^0.12.5", + "@types/d3-scale": "^4.0.9", + "@types/d3-shape": "^3.1.7", + "@types/d3-time": "^3.0.4", + "@types/d3-timer": "^3.0.2", + "d3-color": "^3.1.0", + "d3-interpolate": "^3.0.1", + "d3-path": "^3.1.0", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "d3-time": "^3.1.0", + "d3-timer": "^3.0.1", + "flatqueue": "^3.0.0" + } + }, "node_modules/@mui/x-date-pickers": { "version": "8.22.1", "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.22.1.tgz", @@ -1438,6 +1501,15 @@ } } }, + "node_modules/@mui/x-internal-gestures": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mui/x-internal-gestures/-/x-internal-gestures-0.3.6.tgz", + "integrity": "sha512-IDGCpitIwdBSJpp7wQ4f8C3IwB5u5Ru1fy88JMC4hCgwpUvtvUPyR4Ue5Xt3sbU2J0VEykRwi82h9gFFZeNF5A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + } + }, "node_modules/@mui/x-internals": { "version": "8.22.0", "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.22.0.tgz", @@ -1795,6 +1867,81 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-sankey": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/d3-sankey/-/d3-sankey-0.12.5.tgz", + "integrity": "sha512-/3RZSew0cLAtzGQ+C89hq/Rp3H20QJuVRSqFy6RKLe7E0B8kd2iOS1oBsodrgds4PcNVpqWhdUEng/SHvBcJ6Q==", + "license": "MIT", + "dependencies": { + "@types/d3-shape": "^1" + } + }, + "node_modules/@types/d3-sankey/node_modules/@types/d3-path": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.11.tgz", + "integrity": "sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==", + "license": "MIT" + }, + "node_modules/@types/d3-sankey/node_modules/@types/d3-shape": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.12.tgz", + "integrity": "sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "^1" + } + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1954,6 +2101,12 @@ "dev": true, "license": "MIT" }, + "node_modules/bezier-easing": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", + "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2134,6 +2287,118 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/dayjs": { "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", @@ -2517,6 +2782,12 @@ "node": ">=16" } }, + "node_modules/flatqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-3.0.0.tgz", + "integrity": "sha512-y1deYaVt+lIc/d2uIcWDNd0CrdQTO5xoCjeFdhX0kSXvm2Acm0o+3bAOiYklTEoRyzwio3sv3/IiBZdusbAe2Q==", + "license": "ISC" + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", @@ -2657,6 +2928,15 @@ "node": ">=0.8.19" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5e6bbf2..b02a0a1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.6", + "@mui/x-charts": "^8.22.1", "@mui/x-date-pickers": "^8.22.1", "dayjs": "^1.11.19", "react": "^19.0.0", diff --git a/frontend/src/components/OrdersDashboard.jsx b/frontend/src/components/OrdersDashboard.jsx index f19f530..c472b6d 100644 --- a/frontend/src/components/OrdersDashboard.jsx +++ b/frontend/src/components/OrdersDashboard.jsx @@ -1,3 +1,118 @@ -const OrdersDashboard = () => {} +import Card from '@mui/material/Card'; +import Typography from "@mui/material/Typography"; +import CardContent from '@mui/material/CardContent'; +import { BarChart } from '@mui/x-charts/BarChart'; +import { PieChart } from '@mui/x-charts/PieChart'; +import Box from '@mui/material/Box'; +import Paper from '@mui/material/Paper'; +import Grid from '@mui/material/Grid'; +import dayjs from 'dayjs'; -export default OrdersDashboard; \ No newline at end of file + +const OrdersDashboard = ({ orders }) => { + + const pendingOrders = orders.filter((order) => order.status === 'pending'); + + const ordersByMonth = orders.reduce((acc, order) => { + const monthYear = order.due && dayjs(order.due).isValid() + ? dayjs(order.due).format('MMM YYYY') + : 'no due date'; + acc[monthYear] = (acc[monthYear] || 0) + 1; + return acc; + }, {}); + + const sortedMonths = Object.keys(ordersByMonth).sort((a, b) => { + if (a === 'no due date') return 1; + if (b === 'no due date') return -1; + return dayjs(a, 'MMM YYYY').valueOf() - dayjs(b, 'MMM YYYY').valueOf(); + }); + + const monthLabels = sortedMonths; + const monthCounts = sortedMonths.map(month => ordersByMonth[month]); + + const countByStatus = (statusString) => { + const filteredByStatus = orders.filter((order) => order.status === statusString); + return filteredByStatus.length; + }; + + return ( + + + {/* Total Orders Card */} + + + + + Total Orders + + + {orders.length} + + + + + + + Pending Orders + + + {pendingOrders.length} + + + + + + {/* Orders status pie */} + + + + + Orders by Status + + + + + + + + + {/* Orders month-quantity Barchart */} + + + + + Orders by Due Date Month + + + + + + + + ); +} + +export default OrdersDashboard; 3 \ No newline at end of file diff --git a/frontend/src/components/pages/OrdersPage.jsx b/frontend/src/components/pages/OrdersPage.jsx index 27b95b5..9823d8a 100644 --- a/frontend/src/components/pages/OrdersPage.jsx +++ b/frontend/src/components/pages/OrdersPage.jsx @@ -32,7 +32,7 @@ const OrdersPage = () => { Orders - +