diff --git a/.gitignore b/.gitignore index 92e6794..ade7172 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,7 @@ __pycache__/ # OS .DS_Store + +# Flask instance folder (local DB, secrets) +backend/instance/ +instance/ diff --git a/README.md b/README.md index 776f3d8..094fb3d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # Flashify e-commerce platform for instant deliveries within 20 mins. + + +To download dependencies and create db locally: + +pip install -r requirements.txt +flask db init / create_all \ No newline at end of file diff --git a/backend/Requirements.txt b/backend/Requirements.txt new file mode 100644 index 0000000..3eaab8f --- /dev/null +++ b/backend/Requirements.txt @@ -0,0 +1,5 @@ +Flask==3.1.2 +Flask-Cors==6.0.2 +Flask-JWT-Extended==4.7.1 +Flask-SQLAlchemy==3.1.1 +python-dotenv==1.2.1 \ No newline at end of file diff --git a/backend/app/__init__.py b/backend/app/__init__.py index 5b8b022..10cf24b 100644 --- a/backend/app/__init__.py +++ b/backend/app/__init__.py @@ -1,23 +1,43 @@ from flask import Flask from flask_cors import CORS from flask_sqlalchemy import SQLAlchemy +from flask_jwt_extended import JWTManager db = SQLAlchemy() +jwt = JWTManager() def create_app(): app = Flask(__name__) app.config.from_object("config.Config") - CORS(app) # allow frontend calls - db.init_app(app) # database hookup + # Initialize extensions FIRST + db.init_app(app) + jwt.init_app(app) + + # Apply CORS BEFORE registering blueprints + CORS(app) + # Ensure models are imported before create_all + from . import models # noqa: F401 - @app.route("/health") - def health(): - return {"status": "ok"} + # Create DB tables + with app.app_context(): + db.create_all() + # Register API routes (Blueprint) + from .routes.auth_routes import api + from .routes.location_routes import location_bp + from .routes.address_routes import address_bp - from .routes import api app.register_blueprint(api) + app.register_blueprint(location_bp) + app.register_blueprint(address_bp) - return app + #Debug: Print registered routes + print("\n=== REGISTERED ROUTES ===") + for r in app.url_map.iter_rules(): + print(r) + print("========================\n") + + + return app \ No newline at end of file diff --git a/backend/app/models.py b/backend/app/models.py index 38d00e7..8f38c83 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,5 +1,59 @@ +from datetime import datetime +from werkzeug.security import generate_password_hash, check_password_hash from . import db + class User(db.Model): + __tablename__ = "users" + + id = db.Column(db.Integer, primary_key=True) + + email = db.Column(db.String(255), unique=True, nullable=False, index=True) + password_hash = db.Column(db.String(255), nullable=False) + + # Location selection + location_label = db.Column(db.String(255), nullable=True) + location_lat = db.Column(db.Float, nullable=True) + location_lng = db.Column(db.Float, nullable=True) + + # relationship (optional but recommended) + addresses = db.relationship( + "Address", + backref="user", + lazy=True, + cascade="all, delete-orphan", + ) + + def set_password(self, password: str) -> None: + self.password_hash = generate_password_hash(password) + + def check_password(self, password: str) -> bool: + return check_password_hash(self.password_hash, password) + + +# NEW TABLE +class Address(db.Model): + __tablename__ = "addresses" + id = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(120), unique=True, nullable=False) + + user_id = db.Column( + db.Integer, + db.ForeignKey("users.id"), + nullable=False, + index=True, + ) + + label = db.Column(db.String(50), nullable=False) # Home / Work / Other + line1 = db.Column(db.String(255), nullable=False) + line2 = db.Column(db.String(255), nullable=True) + city = db.Column(db.String(100), nullable=False) + state = db.Column(db.String(100), nullable=False) + zip = db.Column(db.String(20), nullable=False) + + lat = db.Column(db.Float, nullable=True) + lng = db.Column(db.Float, nullable=True) + + is_default = db.Column(db.Boolean, default=False, nullable=False) + + created_at = db.Column(db.DateTime, default=datetime.utcnow) diff --git a/backend/app/routes.py b/backend/app/routes.py deleted file mode 100644 index f367375..0000000 --- a/backend/app/routes.py +++ /dev/null @@ -1,7 +0,0 @@ -from flask import Blueprint, jsonify - -api = Blueprint("api", __name__) - -@api.route("/api/health") -def health(): - return jsonify({"status": "backend running"}) diff --git a/backend/app/routes/address_routes.py b/backend/app/routes/address_routes.py new file mode 100644 index 0000000..ea6b2c1 --- /dev/null +++ b/backend/app/routes/address_routes.py @@ -0,0 +1,99 @@ +from flask import Blueprint, request, jsonify +from flask_jwt_extended import jwt_required, get_jwt_identity + +from .. import db +from ..models import Address + +address_bp = Blueprint("addresses", __name__, url_prefix="/api/addresses") + +@address_bp.before_request +def handle_preflight(): + if request.method == "OPTIONS": + return "", 204 + +@address_bp.route("", methods=["GET"]) +@jwt_required() +def list_addresses(): + user_id = get_jwt_identity() + addresses = ( + Address.query.filter_by(user_id=user_id) + .order_by(Address.id.desc()) + .all() + ) + return jsonify([serialize_address(a) for a in addresses]), 200 + + +@address_bp.route("", methods=["POST"]) +@jwt_required() +def create_address(): + user_id = get_jwt_identity() + data = request.get_json() or {} + + required = ["label", "line1", "city", "state", "zip"] + missing = [k for k in required if not data.get(k)] + if missing: + return jsonify({"message": f"Missing fields: {', '.join(missing)}"}), 400 + + is_default = bool(data.get("is_default", False)) + if is_default: + Address.query.filter_by(user_id=user_id, is_default=True).update({"is_default": False}) + + addr = Address( + user_id=user_id, + label=data["label"], + line1=data["line1"], + line2=data.get("line2"), + city=data["city"], + state=data["state"], + zip=data["zip"], + lat=data.get("lat"), + lng=data.get("lng"), + is_default=is_default, + ) + db.session.add(addr) + db.session.commit() + + return jsonify(serialize_address(addr)), 201 + + +@address_bp.route("//default", methods=["PATCH"]) +@jwt_required() +def set_default_address(address_id): + user_id = get_jwt_identity() + addr = Address.query.filter_by(id=address_id, user_id=user_id).first() + if not addr: + return jsonify({"message": "Address not found"}), 404 + + Address.query.filter_by(user_id=user_id, is_default=True).update({"is_default": False}) + addr.is_default = True + db.session.commit() + + return jsonify({"message": "Default address updated"}), 200 + + +@address_bp.route("/", methods=["DELETE"]) +@jwt_required() +def delete_address(address_id): + user_id = get_jwt_identity() + addr = Address.query.filter_by(id=address_id, user_id=user_id).first() + if not addr: + return jsonify({"message": "Address not found"}), 404 + + db.session.delete(addr) + db.session.commit() + return jsonify({"message": "Deleted"}), 200 + + +def serialize_address(a: Address): + return { + "id": a.id, + "label": a.label, + "line1": a.line1, + "line2": a.line2, + "city": a.city, + "state": a.state, + "zip": a.zip, + "lat": a.lat, + "lng": a.lng, + "is_default": a.is_default, + } \ No newline at end of file diff --git a/backend/app/routes/auth_routes.py b/backend/app/routes/auth_routes.py new file mode 100644 index 0000000..b905328 --- /dev/null +++ b/backend/app/routes/auth_routes.py @@ -0,0 +1,56 @@ +from flask import Blueprint, request, jsonify +from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity +from ..models import User +from .. import db + +api = Blueprint("api", __name__) + +@api.post("/api/auth/signup") +def signup(): + data = request.get_json(silent=True) or {} + email = (data.get("email") or "").strip().lower() + password = data.get("password") or "" + + if not email or not password: + return jsonify(message="Email and password are required."), 400 + + existing = User.query.filter_by(email=email).first() + if existing: + return jsonify(message="User already exists."), 409 + + user = User(email=email) + user.set_password(password) + + db.session.add(user) + db.session.commit() + + return jsonify(message="Signup successful."), 201 + + +@api.post("/api/auth/login") +def login(): + print() + data = request.get_json(silent=True) or {} + email = (data.get("email") or "").strip().lower() + password = data.get("password") or "" + + print(f"[LOGIN] Request data: email={email}, password={'*' * len(password)}") # ✅ ADD + + if not email or not password: + return jsonify(message="Email and password are required."), 400 + + user = User.query.filter_by(email=email).first() + if not user or not user.check_password(password): + return jsonify(message="Invalid email or password."), 401 + + # JWT identity can be email for now + token = create_access_token(identity=email) + + return jsonify(token=token), 200 + + +@api.get("/api/me") +@jwt_required() +def me(): + email = get_jwt_identity() + return jsonify(email=email), 200 diff --git a/backend/app/routes/location_routes.py b/backend/app/routes/location_routes.py new file mode 100644 index 0000000..74595f3 --- /dev/null +++ b/backend/app/routes/location_routes.py @@ -0,0 +1,37 @@ +from flask import Blueprint, request, jsonify +from flask_jwt_extended import jwt_required, get_jwt_identity + +from .. import db +from ..models import User + +location_bp = Blueprint("location", __name__, url_prefix="/api/location") + + +@location_bp.route("/select", methods=["POST", "OPTIONS"]) + +def select_location(): + user_id = get_jwt_identity() + data = request.get_json() or {} + + label = data.get("label") + lat = data.get("lat") + lng = data.get("lng") + + if not label: + return jsonify({"message": "label is required"}), 400 + + user = User.query.get(user_id) + if not user: + return jsonify({"message": "User not found"}), 404 + + user.location_label = label + user.location_lat = lat + user.location_lng = lng + db.session.commit() + + return jsonify( + { + "message": "Location saved", + "location": {"label": user.location_label, "lat": user.location_lat, "lng": user.location_lng}, + } + ), 200 diff --git a/frontend/package.json b/frontend/package.json index c902fa7..1dc669b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "7.3.7", + "@mui/material": "7.3.7", + "@toolpad/core": "^0.16.0", + "axios": "^1.13.4", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.0" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 30536f0..d64ddd5 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,12 +8,33 @@ importers: .: dependencies: + '@emotion/react': + specifier: ^11.14.0 + version: 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': + specifier: ^11.14.1 + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/icons-material': + specifier: 7.3.7 + version: 7.3.7(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/material': + specifier: 7.3.7 + version: 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@toolpad/core': + specifier: ^0.16.0 + version: 0.16.0(b10d5de02e195908768946ebbb476668) + axios: + specifier: ^1.13.4 + version: 1.13.4 react: specifier: ^19.2.0 version: 19.2.3 react-dom: specifier: ^19.2.0 version: 19.2.3(react@19.2.3) + react-router-dom: + specifier: ^7.13.0 + version: 7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) devDependencies: '@eslint/js': specifier: ^9.39.1 @@ -29,7 +50,7 @@ importers: version: 19.2.3(@types/react@19.2.7) '@vitejs/plugin-react-swc': specifier: ^4.2.2 - version: 4.2.2(vite@7.3.0(@types/node@24.10.4)) + version: 4.2.2(vite@7.3.0(@types/node@24.10.4)(yaml@2.5.1)) eslint: specifier: ^9.39.1 version: 9.39.2 @@ -50,7 +71,7 @@ importers: version: 8.50.0(eslint@9.39.2)(typescript@5.9.3) vite: specifier: ^7.2.4 - version: 7.3.0(@types/node@24.10.4) + version: 7.3.0(@types/node@24.10.4)(yaml@2.5.1) packages: @@ -109,6 +130,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -121,6 +146,60 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} @@ -347,6 +426,166 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mui/core-downloads-tracker@7.3.7': + resolution: {integrity: sha512-8jWwS6FweMkpyRkrJooamUGe1CQfO1yJ+lM43IyUJbrhHW/ObES+6ry4vfGi8EKaldHL3t3BG1bcLcERuJPcjg==} + + '@mui/icons-material@7.3.7': + resolution: {integrity: sha512-3Q+ulAqG+A1+R4ebgoIs7AccaJhIGy+Xi/9OnvX376jQ6wcy+rz4geDGrxQxCGzdjOQr4Z3NgyFSZCz4T999lA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^7.3.7 + '@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 + + '@mui/material@7.3.7': + resolution: {integrity: sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^7.3.7 + '@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 + + '@mui/private-theming@7.3.7': + resolution: {integrity: sha512-w7r1+CYhG0syCAQUWAuV5zSaU2/67WA9JXUderdb7DzCIJdp/5RmJv6L85wRjgKCMsxFF0Kfn0kPgPbPgw/jdw==} + engines: {node: '>=14.0.0'} + 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 + + '@mui/styled-engine@7.3.7': + resolution: {integrity: sha512-y/QkNXv6cF6dZ5APztd/dFWfQ6LHKPx3skyYO38YhQD4+Cxd6sFAL3Z38WMSSC8LQz145Mpp3CcLrSCLKPwYAg==} + engines: {node: '>=14.0.0'} + 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 + + '@mui/system@7.3.7': + resolution: {integrity: sha512-DovL3k+FBRKnhmatzUMyO5bKkhMLlQ9L7Qw5qHrre3m8zCZmE+31NDVBFfqrbrA7sq681qaEIHdkWD5nmiAjyQ==} + engines: {node: '>=14.0.0'} + 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 + + '@mui/types@7.4.10': + resolution: {integrity: sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@7.3.7': + resolution: {integrity: sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==} + engines: {node: '>=14.0.0'} + 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 + + '@mui/x-data-grid@8.26.0': + resolution: {integrity: sha512-6WDDTYv9ryce7j3S7bPIRMCHvzG8rbU2O/p9/JK+InoZUAL9BaAKwvIjt31KeE0Q5yHiNDd0FWiOUY1vEcoDog==} + 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 + + '@mui/x-date-pickers@8.26.0': + resolution: {integrity: sha512-tW9SrY8jRX0qf/v4ki/x46hsHgJrsFib271QYWOxTEofREWpPrYqdUarRGs21uhHha0spCzJUU1jEAxFKc3LHQ==} + 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 + 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 + + '@mui/x-internals@8.26.0': + resolution: {integrity: sha512-B9OZau5IQUvIxwpJZhoFJKqRpmWf5r0yMmSXjQuqb5WuqM755EuzWJOenY48denGoENzMLT8hQpA0hRTeU2IPA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@mui/x-virtualizer@0.3.3': + resolution: {integrity: sha512-6ugUh7UAhQYdgPgHLu181zqufh3Y8IqEU9Pe6Huzj0xkRi3NwMx/ZzvrHf2WazNOh2uLhQ5ZM2wFqDu3mxBWZA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@rolldown/pluginutils@1.0.0-beta.47': resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} @@ -535,6 +774,32 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + '@toolpad/core@0.16.0': + resolution: {integrity: sha512-/Iiyubp1u8L4DZqIgP1N0mhOxXpk9+I05xg+Hbk7VYUVEMlVmF+x7+FzPxocOWrvgAdeNZfyCZhY9u99SEQbtQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/cache': ^11 + '@emotion/react': ^11 + '@mui/icons-material': ^7.0.0-beta || ^7.0.0 + '@mui/material': ^7.0.0-beta || ^7.0.0 + '@tanstack/react-router': ^1 + next: ^14 || ^15 + react: ^18 || ^19 + react-dom: ^18 || ^19 + react-router: ^7 + peerDependenciesMeta: + '@tanstack/react-router': + optional: true + next: + optional: true + react-router: + optional: true + + '@toolpad/utils@0.16.0': + resolution: {integrity: sha512-nqM7lk32OJwPFwEJyUoZ1Wvm1qwTlJ+ZP8w+sF9U5rW+ufcEm09sav4h+gwdjx6199B765omqu3AS41nvkWBBw==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -544,11 +809,22 @@ packages: '@types/node@24.10.4': resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} @@ -634,9 +910,22 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.4: + resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -655,6 +944,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -666,6 +959,21 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clipboardy@4.0.0: + resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} + engines: {node: '>=18'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -673,12 +981,27 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -686,6 +1009,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -698,9 +1024,39 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -767,9 +1123,16 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-json-patch@3.1.1: + resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -789,6 +1152,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -800,15 +1166,43 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -821,16 +1215,39 @@ packages: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -847,6 +1264,21 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -855,6 +1287,23 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + is64bit@2.0.0: + resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -873,6 +1322,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -891,6 +1343,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -898,9 +1353,32 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -922,6 +1400,22 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + oppa@0.4.0: + resolution: {integrity: sha512-DFvM3+F+rB/igo3FRnkDWitjZgBH9qZAn68IacYHsqbZBKwuTA+LdD4zSJiQtgQpWq7M08we5FlGAVHz0yW7PQ==} + engines: {node: '>=10'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -938,6 +1432,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -946,6 +1444,20 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -961,6 +1473,17 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -970,14 +1493,51 @@ packages: peerDependencies: react: ^19.2.3 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + + react-router-dom@7.13.0: + resolution: {integrity: sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.13.0: + resolution: {integrity: sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@19.2.3: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + rollup@4.53.5: resolution: {integrity: sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -995,6 +1555,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1003,22 +1566,49 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + system-architecture@0.1.0: + resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + title@4.0.1: + resolution: {integrity: sha512-xRnPkJx9nvE5MF6LkB5e8QJjE2FW8269wTu/LQdf7zZqBgPly0QJPf/CWAo7srj5so4yXfoLEdCFgurlpi47zg==} + hasBin: true + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -1053,6 +1643,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + vite@7.3.0: resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1105,6 +1700,20 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml-diff-patch@2.0.0: + resolution: {integrity: sha512-RhfIQPGcKSZhsUmsczXAeg5jNhWXk3tAmhl2kjfZthdyaL0XXXOpvRozUp22HvPStmZsHu8T30/UEfX9oIwGxw==} + engines: {node: '>=14'} + hasBin: true + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1197,6 +1806,8 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/runtime@7.28.6': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -1220,6 +1831,89 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.6 + '@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.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + 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 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@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(react@19.2.3) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.7 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) + '@emotion/utils': 1.4.2 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.7 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)': + dependencies: + react: 19.2.3 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.27.2': optional: true @@ -1374,6 +2068,154 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mui/core-downloads-tracker@7.3.7': {} + + '@mui/icons-material@7.3.7(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/material': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.7 + + '@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/core-downloads-tracker': 7.3.7 + '@mui/system': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/types': 7.4.10(@types/react@19.2.7) + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.2.7) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 19.2.4 + react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@types/react': 19.2.7 + + '@mui/private-theming@7.3.7(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + prop-types: 15.8.1 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.7 + + '@mui/styled-engine@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 19.2.3 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + + '@mui/system@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/private-theming': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@mui/styled-engine': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(react@19.2.3) + '@mui/types': 7.4.10(@types/react@19.2.7) + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 19.2.3 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@types/react': 19.2.7 + + '@mui/types@7.4.10(@types/react@19.2.7)': + dependencies: + '@babel/runtime': 7.28.6 + optionalDependencies: + '@types/react': 19.2.7 + + '@mui/utils@7.3.7(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/types': 7.4.10(@types/react@19.2.7) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.2.3 + react-is: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@mui/x-data-grid@8.26.0(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/material': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@mui/system': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@mui/x-internals': 8.26.0(@types/react@19.2.7)(react@19.2.3) + '@mui/x-virtualizer': 0.3.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + + '@mui/x-date-pickers@8.26.0(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(dayjs@1.11.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/material': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@mui/system': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@mui/x-internals': 8.26.0(@types/react@19.2.7)(react@19.2.3) + '@types/react-transition-group': 4.4.12(@types/react@19.2.7) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + dayjs: 1.11.13 + transitivePeerDependencies: + - '@types/react' + + '@mui/x-internals@8.26.0(@types/react@19.2.7)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + react: 19.2.3 + reselect: 5.1.1 + use-sync-external-store: 1.6.0(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + + '@mui/x-virtualizer@0.3.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@mui/x-internals': 8.26.0(@types/react@19.2.7)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + + '@popperjs/core@2.11.8': {} + '@rolldown/pluginutils@1.0.0-beta.47': {} '@rollup/rollup-android-arm-eabi@4.53.5': @@ -1494,6 +2336,47 @@ snapshots: dependencies: '@swc/counter': 0.1.3 + '@toolpad/core@0.16.0(b10d5de02e195908768946ebbb476668)': + dependencies: + '@babel/runtime': 7.28.6 + '@emotion/cache': 11.14.0 + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@mui/icons-material': 7.3.7(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@mui/material': 7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@mui/utils': 7.3.7(@types/react@19.2.7)(react@19.2.3) + '@mui/x-data-grid': 8.26.0(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@mui/x-date-pickers': 8.26.0(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@7.3.7(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(dayjs@1.11.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@toolpad/utils': 0.16.0(react@19.2.3) + client-only: 0.0.1 + dayjs: 1.11.13 + invariant: 2.2.4 + path-to-regexp: 6.3.0 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + react-router: 7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@emotion/styled' + - '@mui/system' + - '@types/react' + - date-fns + - date-fns-jalali + - luxon + - moment + - moment-hijri + - moment-jalaali + + '@toolpad/utils@0.16.0(react@19.2.3)': + dependencies: + invariant: 2.2.4 + prettier: 3.5.3 + react: 19.2.3 + react-is: 19.2.4 + title: 4.0.1 + yaml: 2.5.1 + yaml-diff-patch: 2.0.0 + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -1502,10 +2385,18 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 + '@types/react-transition-group@4.4.12(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -1601,11 +2492,11 @@ snapshots: '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-react-swc@4.2.2(vite@7.3.0(@types/node@24.10.4))': + '@vitejs/plugin-react-swc@4.2.2(vite@7.3.0(@types/node@24.10.4)(yaml@2.5.1))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.47 '@swc/core': 1.15.5 - vite: 7.3.0(@types/node@24.10.4) + vite: 7.3.0(@types/node@24.10.4)(yaml@2.5.1) transitivePeerDependencies: - '@swc/helpers' @@ -1626,8 +2517,26 @@ snapshots: dependencies: color-convert: 2.0.1 + arg@5.0.2: {} + argparse@2.0.1: {} + asynckit@0.4.0: {} + + axios@1.13.4: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.6 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + balanced-match@1.0.2: {} baseline-browser-mapping@2.9.8: {} @@ -1649,6 +2558,11 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} caniuse-lite@1.0.30001760: {} @@ -1658,16 +2572,44 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + + client-only@0.0.1: {} + + clipboardy@4.0.0: + dependencies: + execa: 8.0.1 + is-wsl: 3.1.0 + is64bit: 2.0.0 + + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + concat-map@0.0.1: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} + cookie@1.1.1: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1676,14 +2618,48 @@ snapshots: csstype@3.2.3: {} + dayjs@1.11.13: {} + debug@4.4.3: dependencies: ms: 2.1.3 deep-is@0.1.4: {} + delayed-stream@1.0.0: {} + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.6 + csstype: 3.2.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + electron-to-chromium@1.5.267: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -1798,8 +2774,22 @@ snapshots: esutils@2.0.3: {} + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + fast-deep-equal@3.1.3: {} + fast-json-patch@3.1.1: {} + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -1812,6 +2802,8 @@ snapshots: dependencies: flat-cache: 4.0.1 + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -1824,11 +2816,43 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: + 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 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@8.0.1: {} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -1837,14 +2861,32 @@ snapshots: globals@16.5.0: {} + gopd@1.2.0: {} + has-flag@4.0.0: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + hermes-estree@0.25.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + human-signals@5.0.0: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -1856,12 +2898,38 @@ snapshots: imurmurhash@0.1.4: {} + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-stream@3.0.0: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + is64bit@2.0.0: + dependencies: + system-architecture: 0.1.0 + isexe@2.0.0: {} js-tokens@4.0.0: {} @@ -1874,6 +2942,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -1889,16 +2959,34 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.merge@4.6.2: {} + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + lru-cache@5.1.1: dependencies: yallist: 3.1.1 + math-intrinsics@1.1.0: {} + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@4.0.0: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -1915,6 +3003,20 @@ snapshots: node-releases@2.0.27: {} + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + oppa@0.4.0: + dependencies: + chalk: 4.1.2 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -1936,10 +3038,25 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + path-exists@4.0.0: {} path-key@3.1.1: {} + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-to-regexp@6.3.0: {} + + path-type@4.0.0: {} + picocolors@1.1.1: {} picomatch@4.0.3: {} @@ -1952,6 +3069,16 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.5.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} react-dom@19.2.3(react@19.2.3): @@ -1959,10 +3086,45 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 + react-is@16.13.1: {} + + react-is@19.2.4: {} + + react-router-dom@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-router: 7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + + react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + cookie: 1.1.1 + react: 19.2.3 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + + react-transition-group@4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.28.6 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react@19.2.3: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + rollup@4.53.5: dependencies: '@types/estree': 1.0.8 @@ -1997,25 +3159,45 @@ snapshots: semver@7.7.3: {} + set-cookie-parser@2.7.2: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + signal-exit@4.1.0: {} + source-map-js@1.2.1: {} + source-map@0.5.7: {} + + strip-final-newline@3.0.0: {} + strip-json-comments@3.1.1: {} + stylis@4.2.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + + system-architecture@0.1.0: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + title@4.0.1: + dependencies: + arg: 5.0.2 + chalk: 5.6.2 + clipboardy: 4.0.0 + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -2049,7 +3231,11 @@ snapshots: dependencies: punycode: 2.3.1 - vite@7.3.0(@types/node@24.10.4): + use-sync-external-store@1.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + + vite@7.3.0(@types/node@24.10.4)(yaml@2.5.1): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -2060,6 +3246,7 @@ snapshots: optionalDependencies: '@types/node': 24.10.4 fsevents: 2.3.3 + yaml: 2.5.1 which@2.0.2: dependencies: @@ -2069,6 +3256,16 @@ snapshots: yallist@3.1.1: {} + yaml-diff-patch@2.0.0: + dependencies: + fast-json-patch: 3.1.1 + oppa: 0.4.0 + yaml: 2.5.1 + + yaml@1.10.2: {} + + yaml@2.5.1: {} + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.2.1): diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8172e6d..1d16f1f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,8 +1,47 @@ -// src/App.tsx +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Home from "./pages/Home"; +import Login from "./pages/Login"; +import Signup from "./pages/Signup"; +import ProtectedRoute from "./components/ProtectedRoute"; +import ChooseLocation from "./pages/ChooseLocation"; +import ManageAddresses from "./pages/ManageAddresses"; function App() { - return ; + return ( + + + + + + } + /> + + {/* ✅ new pages */} + + + + } + /> + + + + } + /> + + } /> + } /> + + + ); } -export default App; \ No newline at end of file +export default App; diff --git a/frontend/src/assets/grocery-bg.jpg b/frontend/src/assets/grocery-bg.jpg new file mode 100644 index 0000000..9b42b44 Binary files /dev/null and b/frontend/src/assets/grocery-bg.jpg differ diff --git a/frontend/src/components/LoginUtilities.tsx b/frontend/src/components/LoginUtilities.tsx new file mode 100644 index 0000000..2b465a4 --- /dev/null +++ b/frontend/src/components/LoginUtilities.tsx @@ -0,0 +1,135 @@ +import * as React from 'react'; +import { + Button, + FormControl, + Checkbox, + FormControlLabel, + InputLabel, + OutlinedInput, + TextField, + InputAdornment, + Link, + Alert, + IconButton, +} from '@mui/material'; +import AccountCircle from '@mui/icons-material/AccountCircle'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import { useTheme } from '@mui/material/styles'; + +export const providers = [{ id: 'credentials', name: 'Email and Password' }]; + +export function Title() { + return

Login

; +} + +export function Subtitle() { + return ( + + Welcome back! Please log in to continue. + + ); +} + +export function CustomEmailField() { + return ( + + + + ), + }, + }} + /> + ); +} + +export function CustomPasswordField() { + const [show, setShow] = React.useState(false); + + return ( + + Password + + setShow(!show)}> + {show ? : } + + + } + label="Password" + /> + + ); +} + +export function CustomButton() { + return ( + + ); +} + +export function RememberMeCheckbox() { + const theme = useTheme(); + + return ( + } + slotProps={{ + typography: { + fontSize: theme.typography.pxToRem(14), + color: 'text.secondary', + }, + }} + /> + ); +} + +export function SignUpLink() { + return ( + + Sign up + + ); +} + +export function LoginLink() { + return ( + + Already have an account? Log in + + ); +} + +export function ForgotPasswordLink() { + return ( + + Forgot password? + + ); +} diff --git a/frontend/src/components/ProtectedRoute.tsx b/frontend/src/components/ProtectedRoute.tsx new file mode 100644 index 0000000..8e1aa00 --- /dev/null +++ b/frontend/src/components/ProtectedRoute.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { Navigate } from 'react-router-dom'; + +export default function ProtectedRoute({ children }: { children: JSX.Element }) { + const token = localStorage.getItem('token'); + if (!token) return ; + return children; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..65b37a3 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,15 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import { ThemeProvider } from "@mui/material/styles"; +import { CssBaseline } from "@mui/material"; +import theme from "./theme/theme"; -createRoot(document.getElementById('root')!).render( - - - , -) +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + + +); diff --git a/frontend/src/pages/ChooseLocation.tsx b/frontend/src/pages/ChooseLocation.tsx new file mode 100644 index 0000000..5f86c74 --- /dev/null +++ b/frontend/src/pages/ChooseLocation.tsx @@ -0,0 +1,96 @@ +// pages/ChooseLocation.tsx +import { useMemo, useState } from "react" +import { Box, Button, Container, List, ListItemButton, ListItemText, TextField, Typography } from "@mui/material" +import MyLocationIcon from "@mui/icons-material/MyLocation" +import { selectLocation } from "../services/api" +import { useNavigate } from "react-router-dom" + +type Suggestion = { label: string; lat?: number; lng?: number } + +const MOCK_SUGGESTIONS: Suggestion[] = [ + { label: "San Jose, CA", lat: 37.3382, lng: -121.8863 }, + { label: "Sunnyvale, CA", lat: 37.3688, lng: -122.0363 }, + { label: "Santa Clara, CA", lat: 37.3541, lng: -121.9552 }, + { label: "Mountain View, CA", lat: 37.3861, lng: -122.0839 }, +] + +export default function ChooseLocation() { + const navigate = useNavigate() + const [query, setQuery] = useState("") + const [loading, setLoading] = useState(false) + + const filtered = useMemo(() => { + const q = query.trim().toLowerCase() + if (!q) return MOCK_SUGGESTIONS + return MOCK_SUGGESTIONS.filter(s => s.label.toLowerCase().includes(q)) + }, [query]) + + const onPick = async (s: Suggestion) => { + setLoading(true) + try { + await selectLocation(s) + navigate("/addresses") + } finally { + setLoading(false) + } + } + + const useCurrentLocation = () => { + if (!navigator.geolocation) return alert("Geolocation not supported") + + setLoading(true) + navigator.geolocation.getCurrentPosition( + async (pos) => { + try { + const lat = pos.coords.latitude + const lng = pos.coords.longitude + // label is a placeholder. Later: reverse geocode to actual area name. + await selectLocation({ label: `Current location (${lat.toFixed(3)}, ${lng.toFixed(3)})`, lat, lng }) + navigate("/addresses") + } catch (e: any) { + alert(e?.response?.data?.message ?? "Failed to save location") + } finally { + setLoading(false) + } + }, + () => { + setLoading(false) + alert("Could not fetch location permission/coordinates") + } + ) + } + + return ( + + Choose your delivery location + + This helps us show products available near you. + + + + setQuery(e.target.value)} + /> + + + + + {filtered.map((s) => ( + onPick(s)} disabled={loading}> + + + ))} + + + ) +} diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index c7fcdc4..2472e4a 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,21 +1,31 @@ -import { useEffect, useState } from "react"; -import { getHealth } from "../services/api"; +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Box, Button, Typography } from '@mui/material'; function Home() { - const [status, setStatus] = useState("loading..."); + const [status, setStatus] = useState('loading...'); + const navigate = useNavigate(); - useEffect(() => { - getHealth() - .then((data) => setStatus(data.status)) - .catch(() => setStatus("backend not reachable")); - }, []); + + const handleLogout = () => { + localStorage.removeItem('token'); + navigate('/login', { replace: true }); + }; return ( -
-

Frontend ↔ Backend Test

-

Backend status: {status}

-
+ + + Frontend ↔ Backend Test + + + + + Backend status: {status} + ); } export default Home; + diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx new file mode 100644 index 0000000..a942753 --- /dev/null +++ b/frontend/src/pages/Login.tsx @@ -0,0 +1,144 @@ +import * as React from 'react'; +import { Box, Alert } from '@mui/material'; +import { SignInPage, type AuthProvider, type AuthResponse } from '@toolpad/core/SignInPage'; +import groceryBg from '../assets/grocery-bg.jpg'; +import { useNavigate } from 'react-router-dom'; + +import { + Title, + CustomEmailField, + CustomPasswordField, + CustomButton, + RememberMeCheckbox, + SignUpLink, + ForgotPasswordLink, + providers, +} from '../components/LoginUtilities'; + +import {api} from '../services/api'; + + +export default function Login() { + const [error, setError] = React.useState(null); + const navigate = useNavigate(); + + const SubtitleWithErrors = React.useCallback(() => { + if (error) { + return ( + + {error} + + ); + } + return ( + + Welcome back! Please log in to continue. + + ); + }, [error]); + + const handleSignIn = React.useCallback( + async ( + _provider: AuthProvider, + formData?: any, + _callbackUrl?: string, + ): Promise => { + setError(null); + + // Toolpad types formData as `any`, but it is FormData at runtime. + const fd: FormData | null = formData instanceof FormData ? formData : null; + + const email = (fd?.get('email') as string | null) ?? null; + const password = (fd?.get('password') as string | null) ?? null; + + if (!email || !password) { + const msg = 'Please enter both email and password.'; + setError(msg); + return {}; + } + + try { + // ✅ CALL BACKEND + // const res = await api.post('/auth/login', { email, password }); + // return res.data; // { token } + + const res = await api.post('/auth/login', { email, password }); + console.log('Login successful:', res); + + + // ✅ STORE JWT + localStorage.setItem('token', res?.data?.token); + + // Optional: go somewhere after login + // window.location.href = '/'; + navigate('/', { replace: true }) + + return {}; + } catch (err: any) { + // ✅ Show backend message if present + const msg = + err?.response?.data?.message ?? + 'Sign-in failed. Please check your credentials and try again.'; + setError(msg); + return {}; + } + }, + [], + ); + + return ( + + {/* Blurred background */} + + + {/* Dark overlay */} + + + {/* Login card */} + + + + + ); +} diff --git a/frontend/src/pages/ManageAddresses.tsx b/frontend/src/pages/ManageAddresses.tsx new file mode 100644 index 0000000..58abc07 --- /dev/null +++ b/frontend/src/pages/ManageAddresses.tsx @@ -0,0 +1,170 @@ +// pages/ManageAddresses.tsx +import { useEffect, useState } from "react" +import { + Box, Button, Chip, Container, Dialog, DialogActions, DialogContent, DialogTitle, + Divider, IconButton, List, ListItem, ListItemText, MenuItem, Stack, TextField, Typography +} from "@mui/material" +import DeleteIcon from "@mui/icons-material/Delete" +import AddIcon from "@mui/icons-material/Add" +import { createAddress, deleteAddress, getAddresses, setDefaultAddress, type AddressPayload } from "../services/api" + +type Address = AddressPayload & { id: number; is_default: boolean } + +export default function ManageAddresses() { + const [items, setItems] = useState([]) + const [loading, setLoading] = useState(false) + const [open, setOpen] = useState(false) + + const [form, setForm] = useState({ + label: "Home", + line1: "", + line2: "", + city: "", + state: "", + zip: "", + is_default: true, + }) + + const refresh = async () => { + setLoading(true) + try { + const res = await getAddresses() + setItems(res) + } finally { + setLoading(false) + } + } + + useEffect(() => { refresh() }, []) + + const onSave = async () => { + setLoading(true) + try { + await createAddress(form) + setOpen(false) + setForm({ label: "Home", line1: "", line2: "", city: "", state: "", zip: "", is_default: false }) + await refresh() + } catch (e: any) { + alert(e?.response?.data?.message ?? "Failed to create address") + } finally { + setLoading(false) + } + } + + const onMakeDefault = async (id: number) => { + setLoading(true) + try { + await setDefaultAddress(id) + await refresh() + } finally { + setLoading(false) + } + } + + const onDelete = async (id: number) => { + if (!confirm("Delete this address?")) return + setLoading(true) + try { + await deleteAddress(id) + await refresh() + } finally { + setLoading(false) + } + } + + return ( + + + + Your addresses + + Add at least one address to checkout faster. + + + + + + + {items.length === 0 && ( + + No addresses yet. + + )} + + {items.map((a, idx) => ( + + + {!a.is_default && ( + + )} + onDelete(a.id)} disabled={loading}> + + + + } + > + + {a.label} + {a.is_default && } + + } + secondary={`${a.line1}${a.line2 ? ", " + a.line2 : ""}, ${a.city}, ${a.state} ${a.zip}`} + /> + + {idx !== items.length - 1 && } + + ))} + + + {/* Add Address Dialog */} + setOpen(false)} fullWidth maxWidth="sm"> + Add new address + + + setForm(f => ({ ...f, label: e.target.value }))} + > + Home + Work + Other + + + setForm(f => ({ ...f, line1: e.target.value }))} /> + setForm(f => ({ ...f, line2: e.target.value }))} /> + + setForm(f => ({ ...f, city: e.target.value }))} /> + setForm(f => ({ ...f, state: e.target.value }))} /> + + setForm(f => ({ ...f, zip: e.target.value }))} /> + + + setForm(f => ({ ...f, is_default: !f.is_default }))} + /> + + Tap chip to toggle default + + + + + + + + + + + ) +} diff --git a/frontend/src/pages/Signup.tsx b/frontend/src/pages/Signup.tsx new file mode 100644 index 0000000..e4e089f --- /dev/null +++ b/frontend/src/pages/Signup.tsx @@ -0,0 +1,156 @@ +import * as React from 'react'; +import { Box, Alert, Button } from '@mui/material'; +import { SignInPage, type AuthProvider, type AuthResponse } from '@toolpad/core/SignInPage'; +import { useNavigate } from 'react-router-dom'; +import groceryBg from '../assets/grocery-bg.jpg'; + +import { + CustomEmailField, + CustomPasswordField, + RememberMeCheckbox, + LoginLink, + providers, +} from '../components/LoginUtilities'; + +import { signup, login } from '../services/api'; + +function Title() { + return

Sign up

; +} + +function Subtitle({ error }: { error: string | null }) { + if (error) { + return ( + + {error} + + ); + } + + return ( + + Create an account to continue. + + ); +} + +function SubmitButton() { + return ( + + ); +} + +export default function Signup() { + const navigate = useNavigate(); + const [error, setError] = React.useState(null); + + const SubtitleWithErrors = React.useCallback(() => { + return ; + }, [error]); + + const handleSignUp = React.useCallback( + async ( + _provider: AuthProvider, + formData?: any, + _callbackUrl?: string, + ): Promise => { + setError(null); + + const fd: FormData | null = formData instanceof FormData ? formData : null; + const email = (fd?.get('email') as string | null) ?? null; + const password = (fd?.get('password') as string | null) ?? null; + + if (!email || !password) { + const msg = 'Please enter both email and password.'; + setError(msg); + return {}; + } + + try { + // 1) create the account + await signup(email, password); + + // 2) auto-login immediately (consistent UX) + const res = await login(email, password); + localStorage.setItem('token', res.token); + + // 3) go to Home + navigate('/', { replace: true }); + + return {}; + } catch (err: any) { + const msg = + err?.response?.data?.message ?? + 'Signup failed. Please try again.'; + setError(msg); + return {}; + } + }, + [navigate], + ); + + return ( + + {/* Blurred background */} + + + {/* Dark overlay */} + + + {/* Signup card */} + + + + + ); +} diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index feb3efa..7cb0389 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -1,11 +1,90 @@ -const API_BASE_URL = "http://127.0.0.1:5000"; +import axios from 'axios'; -export async function getHealth() { - const response = await fetch(`${API_BASE_URL}/api/health`); +const API_BASE_URL = 'http://127.0.0.1:5000/api'; - if (!response.ok) { - throw new Error("Backend not reachable"); +export const api = axios.create({ + baseURL: API_BASE_URL, + headers: { + 'Content-Type': 'application/json', + }, + // withCredentials: true, +}); + +//Automatically attach JWT to every request (if present) +api.interceptors.request.use((config) => { + const token = localStorage.getItem('token'); + if (token) { + config.headers = config.headers ?? {}; + config.headers.Authorization = `Bearer ${token}`; } + return config; +}); + +//Auth APIs + +export async function login(email: string, password: string) { + const res = await api.post('/auth/login', { email, password }); + return res.data; // { token } +} + +export async function signup(email: string, password: string) { + const res = await api.post('/auth/signup', { email, password }); + return res.data; +} + +// Protected user info +export async function me() { + const res = await api.get('/me'); + return res.data; +} + +// Location APIs +export type LocationPayload = { + label: string; + lat?: number; + lng?: number; +}; + +export async function selectLocation(payload: LocationPayload) { + const res = await api.post("/location/select", payload); + return res.data; // { message, location } +} + +// Address APIs +export type AddressPayload = { + label: "Home" | "Work" | "Other" | string; + line1: string; + line2?: string; + city: string; + state: string; + zip: string; + lat?: number; + lng?: number; + is_default?: boolean; +}; + +export type Address = AddressPayload & { + id: number; + is_default: boolean; + created_at?: string; +}; + +export async function getAddresses() { + const res = await api.get("/addresses"); + return res.data as Address[]; +} + +export async function createAddress(payload: AddressPayload) { + const res = await api.post("/addresses", payload); + return res.data as Address; +} + +export async function setDefaultAddress(addressId: number) { + const res = await api.patch(`/addresses/${addressId}/default`); + return res.data; // { message } +} - return response.json(); +export async function deleteAddress(addressId: number) { + const res = await api.delete(`/addresses/${addressId}`); + return res.data; // { message } } diff --git a/frontend/src/theme/theme.ts b/frontend/src/theme/theme.ts new file mode 100644 index 0000000..115e935 --- /dev/null +++ b/frontend/src/theme/theme.ts @@ -0,0 +1,87 @@ +import { createTheme } from "@mui/material/styles"; + +const theme = createTheme({ + palette: { + mode: "light", + + // Mint primary + primary: { + main: "#39B68B", + contrastText: "#FFFFFF", + }, + + // Lavender accent + secondary: { + main: "#7C74D8", + }, + + success: { + main: "#39B68B", + }, + + background: { + default: "#EAF7F1", // soft mint background + paper: "#FFFFFF", + }, + + text: { + primary: "#2D2A4A", + secondary: "#5B5875", + }, + }, + + // Rounded, modern feel + shape: { + borderRadius: 16, + }, + + typography: { + fontFamily: `"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial`, + h5: { + fontWeight: 800, + }, + button: { + textTransform: "none", + fontWeight: 700, + }, + }, + + components: { + MuiButton: { + styleOverrides: { + root: { + borderRadius: 12, + height: 44, + paddingInline: 16, + }, + containedPrimary: { + boxShadow: "none", + }, + }, + }, + + MuiPaper: { + styleOverrides: { + root: { + borderRadius: 16, + }, + }, + }, + + MuiTextField: { + defaultProps: { + size: "small", + }, + }, + + MuiListItemButton: { + styleOverrides: { + root: { + borderRadius: 14, + }, + }, + }, + }, +}); + +export default theme; diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 259b318..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Flask==3.0.0 -Flask-Cors==4.0.0 -Werkzeug==3.0.1 -click==8.1.7 -itsdangerous==2.1.2 -Jinja2==3.1.3 \ No newline at end of file