From 5ad65c3c59bbdb6dce996db39e07e8cde1f47853 Mon Sep 17 00:00:00 2001 From: mukeshs Date: Wed, 26 Nov 2025 20:01:52 +0530 Subject: [PATCH] improved changes --- Machine-Backend/app/__init__.py | 1 + .../app/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 5028 bytes .../machines_backup_20251125_200112.db | Bin 0 -> 159744 bytes ...machines_before_restore_20251125_200226.db | Bin 0 -> 159744 bytes Machine-Backend/app/instance/machines.db | Bin 53248 -> 159744 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 256 bytes .../models/__pycache__/models.cpython-313.pyc | Bin 14743 -> 28178 bytes .../models/__pycache__/models.cpython-314.pyc | Bin 0 -> 37297 bytes Machine-Backend/app/models/models.py | 359 ++++- .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 193 bytes .../routes/__pycache__/routes.cpython-313.pyc | Bin 102304 -> 135213 bytes .../routes/__pycache__/routes.cpython-314.pyc | Bin 0 -> 155334 bytes Machine-Backend/app/routes/routes.py | 1221 ++++++++++++++++- .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 259 bytes .../__pycache__/services.cpython-313.pyc | Bin 48988 -> 80897 bytes .../__pycache__/services.cpython-314.pyc | Bin 0 -> 96399 bytes Machine-Backend/app/services/services.py | 1028 +++++++++++++- .../04a721feffc64a119d22391e9ba31514_7up.jpg | Bin 0 -> 148568 bytes .../a054b74e7a5c4714bf8199c43c8e58e2_7up.png | Bin 55253 -> 0 bytes ...175757d051d4a36bd435aa6e48d6c77_sample.pdf | Bin 0 -> 18810 bytes Machine-Backend/backup.sql | 0 Machine-Backend/backup_db.py | 255 ++++ Machine-Backend/migration_sqlite.py | 603 ++++++++ Machine-Backend/wait_for_db.py | 145 +- Project/microservice.py | 4 +- .../app/core/services/role-state.service.ts | 78 ++ .../app/mock-api/common/navigation/data.ts | 181 +-- .../branch-list/branch-list.component.html | 327 ++++- .../branch-list/branch-list.component.scss | 336 +++++ .../branch-list/branch-list.component.ts | 321 ++++- .../company/branch-list/branch.service.ts | 140 ++ .../client-list/client-list.component.html | 336 ++++- .../client-list/client-list.component.scss | 428 ++++++ .../client-list/client-list.component.ts | 290 +++- .../client-list/client-list.service.ts | 182 +++ .../company-admin-list.component.html | 269 +++- .../company-admin-list.component.scss | 237 ++++ .../company-admin-list.component.ts | 242 +++- .../company-admin-list.service.ts | 124 ++ .../machine-details.component.html | 122 +- .../machine-details.component.scss | 59 + .../machine-details.component.ts | 205 ++- .../dashboard/machines/machine.service.ts | 189 ++- .../machines-list.component.html | 15 + .../machines-list.component.scss | 715 ++++++---- .../products/brand/brand.component.html | 336 ++++- .../products/brand/brand.component.scss | 397 ++++++ .../products/brand/brand.component.ts | 459 ++++++- .../dashboard/products/brand/brand.service.ts | 149 ++ .../categories/categories.component.html | 330 ++++- .../categories/categories.component.scss | 291 ++++ .../categories/categories.component.ts | 333 ++++- .../products/categories/category.service.ts | 110 ++ .../product-list/product-list.component.html | 176 ++- .../product-list/product-list.component.scss | 167 ++- .../product-list/product-list.component.ts | 127 +- .../dashboard/products/product.service.ts | 64 +- .../sub-categories.component.html | 368 ++++- .../sub-categories.component.scss | 180 +++ .../sub-categories.component.ts | 397 +++++- .../sub-categories/subcategory.service.ts | 94 ++ .../role-management.component.ts | 121 +- .../role-management.service.ts | 75 +- .../admin/dashboard/user/user.component.html | 139 +- .../admin/dashboard/user/user.component.scss | 83 +- .../admin/dashboard/user/user.component.ts | 454 +++++- .../admin/dashboard/user/user.service.ts | 82 +- machine-operations/angular.json | 3 + 68 files changed, 12520 insertions(+), 827 deletions(-) create mode 100644 Machine-Backend/app/__pycache__/__init__.cpython-314.pyc create mode 100644 Machine-Backend/app/instance/backups/machines_backup_20251125_200112.db create mode 100644 Machine-Backend/app/instance/backups/machines_before_restore_20251125_200226.db create mode 100644 Machine-Backend/app/models/__pycache__/__init__.cpython-314.pyc create mode 100644 Machine-Backend/app/models/__pycache__/models.cpython-314.pyc create mode 100644 Machine-Backend/app/routes/__pycache__/__init__.cpython-314.pyc create mode 100644 Machine-Backend/app/routes/__pycache__/routes.cpython-314.pyc create mode 100644 Machine-Backend/app/services/__pycache__/__init__.cpython-314.pyc create mode 100644 Machine-Backend/app/services/__pycache__/services.cpython-314.pyc create mode 100644 Machine-Backend/app/uploads/04a721feffc64a119d22391e9ba31514_7up.jpg delete mode 100644 Machine-Backend/app/uploads/a054b74e7a5c4714bf8199c43c8e58e2_7up.png create mode 100644 Machine-Backend/app/uploads/user_documents/f175757d051d4a36bd435aa6e48d6c77_sample.pdf create mode 100644 Machine-Backend/backup.sql create mode 100644 Machine-Backend/backup_db.py create mode 100644 Machine-Backend/migration_sqlite.py create mode 100644 fuse-starter-v20.0.0/src/app/core/services/role-state.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/products/brand/brand.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/products/categories/category.service.ts create mode 100644 fuse-starter-v20.0.0/src/app/modules/admin/dashboard/products/sub-categories/subcategory.service.ts diff --git a/Machine-Backend/app/__init__.py b/Machine-Backend/app/__init__.py index e2f15e1..92345bb 100644 --- a/Machine-Backend/app/__init__.py +++ b/Machine-Backend/app/__init__.py @@ -53,6 +53,7 @@ def create_app(): app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_ECHO'] = os.getenv('SQLALCHEMY_ECHO', 'False').lower() == 'true' + # Initialize extensions db.init_app(app) diff --git a/Machine-Backend/app/__pycache__/__init__.cpython-314.pyc b/Machine-Backend/app/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8b8f873bf01a84efd06095c4f20ac3f08a665a5 GIT binary patch literal 5028 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 literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/instance/backups/machines_before_restore_20251125_200226.db b/Machine-Backend/app/instance/backups/machines_before_restore_20251125_200226.db new file mode 100644 index 0000000000000000000000000000000000000000..b0cd9ae00396b10a98297ab2b464e1fb6214aa71 GIT binary patch literal 159744 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 literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/instance/machines.db b/Machine-Backend/app/instance/machines.db index eef373b5f9c56750fdfaf0628c4f643228ce5d93..2e1d21aa4acc0cc75312a6378c90d3bf73cae44f 100644 GIT binary patch literal 159744 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 delta 964 zcmc&yTWm~07@pH^+ikmRv`AIO?v|z!c6a8?%$b>W%O({MwP*nE-&e z-hWo(<>jQu1eqh};_w@YHS~h`YxoA<;VyQ$|?e|bHmjTC2QVsFd*G2tF6a*d;%;ToeFH2%0o7omI zZRs;z$<)ro;#nA_5lLb*M^H}x^I~G2TDgF5eGwVNB^KO7CQECWNt1}>7|&snp+dKb zYeypxqzO)SYUu!I%jebS`JpN;NJ-6V{ba=`M{$NoEQtibi2YK!D<976t&C)=;rXs(kyr#y8t@~01t;N`u&sC6--2jFekr{MxdN_9;@tuG|hg~ICLZAKI5m}8Vz6@{d0`Ih3+ zzr$f8MjcLlZZns1Or1brDP2lj;WOczC0?y$DhEgY_=LzHvT_XPh?yZoZhu&8-DTVf zWB0!|hG_N~LtUAA)UN%JNex3hXNnA^goazEcN@*R%cr2zuO-yADJNX^?6}c-vc4_~ wQtm0N?d^E7QeB;HS1+ISs;@8NsxTE*H)ft2JJhG?j(SUw-_Z)QGw<4d06~}xUH||9 diff --git a/Machine-Backend/app/models/__pycache__/__init__.cpython-314.pyc b/Machine-Backend/app/models/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b936a027e56f8cd6330f4aef9441268664e1c4bd GIT binary patch literal 256 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 IC`wcOZ+?dUc>n+a delta 128 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 s({{Dva>qjk!w(Os52{q->U&Ai3%P2Y$&aEdT%j literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/models/models.py b/Machine-Backend/app/models/models.py index b08e403..b94dcf8 100644 --- a/Machine-Backend/app/models/models.py +++ b/Machine-Backend/app/models/models.py @@ -4,7 +4,35 @@ import time import json from werkzeug.security import generate_password_hash, check_password_hash -# Machine Model +class RefillerMachine(db.Model): + __tablename__ = 'refiller_machines' + + id = db.Column(db.Integer, primary_key=True) + refiller_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False) + machine_id = db.Column(db.String(10), db.ForeignKey('machines.machine_id', ondelete='CASCADE'), nullable=False) + assigned_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) + assigned_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + + # Relationships + assigner = db.relationship('User', foreign_keys=[assigned_by], backref='machine_assignments_made') + + # Unique constraint: one refiller can't be assigned to same machine twice + __table_args__ = ( + db.UniqueConstraint('refiller_id', 'machine_id', name='unique_refiller_machine'), + ) + + def to_dict(self): + return { + 'id': self.id, + 'refiller_id': self.refiller_id, + 'machine_id': self.machine_id, + 'assigned_at': self.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if self.assigned_at else None, + 'assigned_by': self.assigned_by, + 'assigned_by_username': self.assigner.username if self.assigner else None + } + + +# Machine Model - UPDATED class Machine(db.Model): __tablename__ = 'machines' @@ -20,10 +48,37 @@ class Machine(db.Model): connection_status = db.Column(db.String(50), nullable=False) created_on = db.Column(db.String(20), nullable=False) password = db.Column(db.String(128), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + + # Relationships slots = db.relationship('VendingSlot', backref='machine', lazy=True) - client = db.relationship('User', backref='machines') + client = db.relationship('User', foreign_keys=[client_id], backref='client_machines') + creator = db.relationship('User', foreign_keys=[created_by], backref='created_machines') + + # ⭐ NEW: Many-to-many relationship with Refillers through RefillerMachine + assigned_refillers = db.relationship( + 'User', + secondary='refiller_machines', + primaryjoin='Machine.machine_id == RefillerMachine.machine_id', + secondaryjoin='and_(User.id == RefillerMachine.refiller_id, User.roles == "Refiller")', + backref='assigned_machines_rel', + viewonly=True + ) def to_dict(self): + # Get assigned refillers + refiller_assignments = RefillerMachine.query.filter_by(machine_id=self.machine_id).all() + assigned_refillers = [] + for assignment in refiller_assignments: + refiller = User.query.get(assignment.refiller_id) + if refiller: + assigned_refillers.append({ + 'id': refiller.id, + 'username': refiller.username, + 'email': refiller.email, + 'assigned_at': assignment.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if assignment.assigned_at else None + }) + return { 'id': self.id, 'machine_id': self.machine_id, @@ -36,7 +91,10 @@ class Machine(db.Model): 'operation_status': self.operation_status, 'connection_status': self.connection_status, 'created_on': self.created_on, - 'password': self.password + 'password': self.password, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'assigned_refillers': assigned_refillers # ⭐ NEW } def set_password(self, password): @@ -46,7 +104,7 @@ class Machine(db.Model): return check_password_hash(self.password, password) -# User Model - UPDATED with proper password hashing +# User Model - UPDATED WITH BOTH CLIENT AND MACHINE ASSIGNMENTS class User(db.Model): __tablename__ = 'users' @@ -57,7 +115,9 @@ class User(db.Model): contact = db.Column(db.String(20), nullable=False) roles = db.Column(db.String(50), nullable=False) user_status = db.Column(db.String(50), nullable=False) - password = db.Column(db.String(255), nullable=False) # Increased length for hash + password = db.Column(db.String(255), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + assigned_to = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # Assigned to Client # File storage fields photo = db.Column(db.String(255), nullable=True) @@ -67,9 +127,27 @@ class User(db.Model): # Timestamps created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + + # Relationships + creator = db.relationship('User', remote_side=[id], backref='created_users', foreign_keys=[created_by]) + assigned_client = db.relationship('User', remote_side=[id], backref='assigned_refillers', foreign_keys=[assigned_to]) def to_dict(self): """Convert user object to dictionary""" + # Get assigned machines for Refillers + assigned_machines = [] + if self.roles == 'Refiller': + machine_assignments = RefillerMachine.query.filter_by(refiller_id=self.id).all() + for assignment in machine_assignments: + machine = Machine.query.filter_by(machine_id=assignment.machine_id).first() + if machine: + assigned_machines.append({ + 'machine_id': machine.machine_id, + 'machine_model': machine.machine_model, + 'branch_name': machine.branch_name, + 'assigned_at': assignment.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if assignment.assigned_at else None + }) + return { 'id': self.id, 'user_id': self.user_id, @@ -83,7 +161,12 @@ class User(db.Model): 'documents': json.loads(self.documents) if self.documents else [], 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None, - 'machines': [m.to_dict() for m in self.machines] if hasattr(self, 'machines') else [] + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'assigned_to': self.assigned_to, + 'assigned_to_username': self.assigned_client.username if self.assigned_client else None, + 'assigned_machines': assigned_machines, # ⭐ NEW + 'assigned_machines_count': len(assigned_machines) # ⭐ NEW } def set_password(self, password): @@ -125,6 +208,13 @@ class Product(db.Model): price = db.Column(db.Float, nullable=False) product_image = db.Column(db.String(255), nullable=False) created_date = db.Column(db.String(20), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + # NEW: Billing and expiration dates + billing_date = db.Column(db.DateTime, nullable=True) + expiration_date = db.Column(db.DateTime, nullable=True) + + # Relationship + creator = db.relationship('User', backref='created_products') def to_dict(self): return { @@ -133,7 +223,11 @@ class Product(db.Model): 'product_name': self.product_name, 'price': str(self.price), 'product_image': self.product_image, - 'created_date': self.created_date + 'created_date': self.created_date, + 'billing_date': self.billing_date.strftime("%Y-%m-%d") if self.billing_date else None, + 'expiration_date': self.expiration_date.strftime("%Y-%m-%d") if self.expiration_date else None, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None } @@ -191,7 +285,9 @@ class Transaction(db.Model): 'return_amount': self.return_amount, 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None } -# Add to your models.py + + +# Role Model class Role(db.Model): __tablename__ = 'roles' @@ -199,15 +295,260 @@ class Role(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) description = db.Column(db.String(255)) permissions = db.Column(db.Text) # JSON string of permission IDs + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + # Relationship + creator = db.relationship('User', backref='created_roles') + def to_dict(self): return { 'id': self.id, 'name': self.name, 'description': self.description, 'permissions': json.loads(self.permissions) if self.permissions else [], + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None - } \ No newline at end of file + } + + +# Branch Model +class Branch(db.Model): + __tablename__ = 'branches' + + id = db.Column(db.Integer, primary_key=True) + branch_id = db.Column(db.String(50), unique=True, nullable=False) + code = db.Column(db.String(20), unique=True, nullable=False) + name = db.Column(db.String(100), nullable=False) + location = db.Column(db.String(100), nullable=False) + address = db.Column(db.Text, nullable=False) + contact = db.Column(db.String(20), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + + # Relationship + creator = db.relationship('User', backref='created_branches') + + def to_dict(self): + return { + 'id': self.id, + 'branch_id': self.branch_id, + 'code': self.code, + 'name': self.name, + 'location': self.location, + 'address': self.address, + 'contact': self.contact, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, + 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None + } + + @staticmethod + def generate_branch_id(): + """Generate unique branch ID with BR prefix""" + import uuid + while True: + branch_id = f"BR{uuid.uuid4().hex[:8].upper()}" + if not Branch.query.filter_by(branch_id=branch_id).first(): + return branch_id + +# Brand Model +class Brand(db.Model): + __tablename__ = 'brands' + + id = db.Column(db.Integer, primary_key=True) + brand_id = db.Column(db.String(50), unique=True, nullable=False) + name = db.Column(db.String(100), nullable=False) + branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False) + branch_name = db.Column(db.String(100), nullable=False) + image = db.Column(db.String(255), nullable=True) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + + # Relationships + branch = db.relationship('Branch', backref='brands', foreign_keys=[branch_id]) + creator = db.relationship('User', backref='created_brands') + + def to_dict(self): + return { + 'id': self.id, + 'brand_id': self.brand_id, + 'name': self.name, + 'branch_id': self.branch_id, + 'branch_name': self.branch_name, + 'image': self.image, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, + 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None + } + + @staticmethod + def generate_brand_id(name): + """ + Generate brand ID from first 3 letters of name + 4-digit sequence + Example: CocaCola -> COC0001, COC0002, etc. + """ + import re + + # Extract only letters from name and get first 3 + letters_only = ''.join(filter(str.isalpha, name)).upper() + prefix = letters_only[:3].ljust(3, 'X') # Pad with X if less than 3 letters + + # Find the highest existing sequence number for this prefix + existing_brands = Brand.query.filter( + Brand.brand_id.like(f"{prefix}%") + ).all() + + if not existing_brands: + sequence = 1 + else: + # Extract sequence numbers and find max + sequences = [] + for brand in existing_brands: + try: + seq_part = brand.brand_id[3:] # Get part after prefix + if seq_part.isdigit(): + sequences.append(int(seq_part)) + except: + continue + + sequence = max(sequences) + 1 if sequences else 1 + + # Format: PREFIX + 4-digit sequence + brand_id = f"{prefix}{sequence:04d}" + + return brand_id + +# Category Model +class Category(db.Model): + __tablename__ = 'categories' + + id = db.Column(db.Integer, primary_key=True) + category_id = db.Column(db.String(50), unique=True, nullable=False) + name = db.Column(db.String(100), nullable=False) + image = db.Column(db.String(255), nullable=True) + brand_id = db.Column(db.String(50), db.ForeignKey('brands.brand_id'), nullable=False) + brand_name = db.Column(db.String(100), nullable=False) + branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False) + branch_name = db.Column(db.String(100), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + + # Relationships + brand = db.relationship('Brand', backref='categories', foreign_keys=[brand_id]) + branch = db.relationship('Branch', backref='categories', foreign_keys=[branch_id]) + creator = db.relationship('User', backref='created_categories') + + def to_dict(self): + return { + 'id': self.id, + 'category_id': self.category_id, + 'name': self.name, + 'image': self.image, + 'brand_id': self.brand_id, + 'brand_name': self.brand_name, + 'branch_id': self.branch_id, + 'branch_name': self.branch_name, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, + 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None + } + + @staticmethod + def generate_category_id(name): + """ + Generate category ID from first 3 letters of name + 4-digit sequence + Example: Beverages -> BEV0001, BEV0002, etc. + """ + import re + + # Extract only letters from name and get first 3 + letters_only = ''.join(filter(str.isalpha, name)).upper() + prefix = letters_only[:3].ljust(3, 'X') # Pad with X if less than 3 letters + + # Find the highest existing sequence number for this prefix + existing_categories = Category.query.filter( + Category.category_id.like(f"{prefix}%") + ).all() + + if not existing_categories: + sequence = 1 + else: + # Extract sequence numbers and find max + sequences = [] + for category in existing_categories: + try: + seq_part = category.category_id[3:] # Get part after prefix + if seq_part.isdigit(): + sequences.append(int(seq_part)) + except: + continue + + sequence = max(sequences) + 1 if sequences else 1 + + # Format: PREFIX + 4-digit sequence + category_id = f"{prefix}{sequence:04d}" + + return category_id + +class SubCategory(db.Model): + __tablename__ = 'sub_categories' + + id = db.Column(db.Integer, primary_key=True) + sub_category_id = db.Column(db.String(50), unique=True, nullable=False) + name = db.Column(db.String(100), nullable=False) + image = db.Column(db.String(255), nullable=True) + category_id = db.Column(db.String(50), db.ForeignKey('categories.category_id'), nullable=False) + category_name = db.Column(db.String(100), nullable=False) + brand_id = db.Column(db.String(50), db.ForeignKey('brands.brand_id'), nullable=False) + brand_name = db.Column(db.String(100), nullable=False) + branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False) + branch_name = db.Column(db.String(100), nullable=False) + created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) + created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) + + # Relationships + category = db.relationship('Category', backref='sub_categories', foreign_keys=[category_id]) + brand = db.relationship('Brand', backref='sub_categories', foreign_keys=[brand_id]) + branch = db.relationship('Branch', backref='sub_categories', foreign_keys=[branch_id]) + creator = db.relationship('User', backref='created_subcategories') + + def to_dict(self): + return { + 'id': self.id, + 'sub_category_id': self.sub_category_id, + 'name': self.name, + 'image': self.image, + 'category_id': self.category_id, + 'category_name': self.category_name, + 'brand_id': self.brand_id, + 'brand_name': self.brand_name, + 'branch_id': self.branch_id, + 'branch_name': self.branch_name, + 'created_by': self.created_by, + 'created_by_username': self.creator.username if self.creator else None, + 'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None, + 'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None + } + + @staticmethod + def generate_sub_category_id(name): + letters_only = ''.join(filter(str.isalpha, name)).upper() + prefix = letters_only[:3].ljust(3, 'X') + existing = SubCategory.query.filter(SubCategory.sub_category_id.like(f"{prefix}%")).all() + if not existing: + sequence = 1 + else: + sequences = [int(s.sub_category_id[3:]) for s in existing if s.sub_category_id[3:].isdigit()] + sequence = max(sequences) + 1 if sequences else 1 + return f"{prefix}{sequence:04d}" \ No newline at end of file diff --git a/Machine-Backend/app/routes/__pycache__/__init__.cpython-314.pyc b/Machine-Backend/app/routes/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c6b2d0064ab710bfdcd9fdf985488cfa16d7925 GIT binary patch literal 193 zcmdPqvwD(#3%EV-N=hn4pZ$LO@0XLmXoeqbGw0V+o@?LlBcPgC_G!MxcZy z<1MD70zXZrTWm%7r6sAwMa)3al?!p7#PI(CMIWO z=B4U7B_?O5=B30W78Jxl^v1-;XXa&=#K-FuRNmsS$<0qG%}KQ@;s6>2va1-x_`uA_ N$atSYtcVTB0RU-^E;|4K literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc b/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc index 47077556b30473a4fdf43c4bd3fabfcd26315658..280d331af148fafd01e08cc5577b92b605f4938f 100644 GIT binary patch delta 35139 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 delta 14192 zcmbVS30$1jwdc+-!!kg~!T^zF213XL5(pt?BMN4R01>j77$p;!z$7q3?hHx5m`S3q zb!qGJ8NX`#o?DZeO{>!YXM)~4^X)~cz#7^_BOYvcCL|9rzY!-V+kU4ylc;bn=*1t7uar^v?}CCXiaFfG>onv zBgfQ_m1FD2$#L~rGOOM$?e*C*o6D^YIWmXpSsQX?F8_{~lEQ=ip>k5hzW)y-a`Xka>l+c>GZo&+co+C@v zm9#V}yI;yu>clupDO2tz$0|L-Zkc3IKO)LyP|p%I*`WSGRGC4QnCzBvgK8yeib3rr zYN|oqPgI3Ly-rl6LD|f9%QSHtx54C)wBa}CO3v0G{kYC2K#49ZQ^1qO8kQ5PE2<3!aO)JdZ13@X=Z zw_IdUi;0?VP<>W=(t_57;}hgUWV>axK{Y1lSS~TBD~VcTP)DfdT7!Cxs7nnh zCB<%8XHYYVy3C+FL|txB3Q=ta^=qQm8`OtHxeY2m)o$5fPz^-28&p409R~GNqC5um zDp4B^%51Y+It^+XQC$YrMwGYH>bhF_S?0yUs+dM!9=?0@EVC$7UK{r?;ohtXVzlx| z)`WblRp|gsB5);Yi5>2sTk6@$=UJ=8^l+>FX(61-_?&qK$k!}^ZhtTkz?ybAWEC3E zW>ETa7KrNbBRSbts{e99nJ7}eD%eT`_7#4XRf@LheZ4e*r=yMgkS=A9qhi4lw007l zI4y_?yR1M)GGH>G43I*gnY%qc&Fu9BWft;18a@z<3W&#?O1*D~QdV+06B^h2_y3<^U z&dy}l^#!{;zM!|=9rXHr4(Zv_=arrgS)!btyp|-`T;@%0pqj2R5s@fQl})iiU9v*? ztgN_-m9-soFM*c2%H7@PStzAnGJBF4g(T!^0+&TrDvjlZrC?IBfGm_^`RKgb+#MZ5 zyvp5Sr99D`&GRlo3uYx{h?V;)3hFVcgt(%^xf0o)Bov~J8q#@W{-P1xb^ALzBdMCo2U0537m`JR@<`R+X!)I9R%DA=eu}d(w~A$Pwp;9#{*JzO8ur)J z&0?Cc3{XM_36zf-?BP9)-xndQt9aA1#h6~y>e8vEmeDRfw9m9f`sUq3H_zcqha46A zs6_aE&4#5h)MS|E#>KeCp$bRCcQ+q0g`W5qIg2cXpry~RDFZ3{}zn_olwJdpspuSYo8W&rDWwZ`u;ad z`DA7B+!*ar6_;6O_Z<75EoT3dFNQB(wL*yJmHn%WD-CX_heGOz!iykj?riTYUj{ob zhyS_yWs&Qo^g!BD-&QzS2gfY%sk8`>ubDA3+GnC3%CL^D(i7p4l)n9Z``h zjqR0!oV51Y;e2a@K-7710g##e8W1P7sBlM?AGJszQ+1g%)7EM9 zUnK>RXQ~uuL6y?qQywxADa`}}W9nO*n;c#4K$instUE}XF-zVaBY74&i+?n@DF^5Q zCQ^-nMD%C@SOG}@4+@fzN&%z-Y=D9^U^;FDd}4RCDh$}qI(%PHg~!}A`4%3}fJ z09gRL((fx0RmzWj<3(Y3(AQ=rjb_Ue*5-4H>Lj>fd9j4gGCC@Slr&o}8B3$z=jmVC zA@h{x{+*euQ`RnPBSCq+e}h<})D9G6)={ro@|Hd~#j3%6IbZP&+}VntUe}ou%(A%` zA;pp%72}{;s2tcwg%y>PAtpIlabJ-a;uQ>KxN>z-arfV1#v@EtONY|iq^I5E-AaKq z^T$$#ytQ%U7JB`@fTxYCQK04ZDPLSs>|BevO+=Nnh{xslO7|bRRH)SL$jOZa_@hXc z*gV?;eeLa@KtNW7x9m7-sbH3wc_+0P$XKpdb8K|ehSQ-7EUKzJrE^a~XaWRS45$YS zB}mm$iozFk5+iRGzb zdV^g(0r?=6oXJ>85ro6%57PSU^L4OiS>cLAeyAaL08Rj|Ac%xS*be#v@@8Z!1hm#I z?Dpji$bz8_q=TQ3&*9nLtDl`Xb7*@Ag4rfISF&^_qZLaBf{89&&Ae$_P_uS;+Wq7` z$DoXt|0JYFU{78Ey%=n1SxVBiuC;8qE2)VqNtgIcs->j{JmjB)ZJRy)S`t|b4x4(P zJcl-_A4nq5#U3%Kw5=ZL-Pqs8?8`;r>#ltuwFu%rliB3wJ^*yCFf8s zyJeb~r`&MMi=sqXzjvaTrF?hq)Ty(Hmp^0|572UY3l;TN| zX(?rgCyM@X%i$+einpP zy7nOL`;Nw;E4q+Ua5T@4Is-N;HuBlBOz3$RdfpA#1Nb+$sf?EM9;GG0z_tA-=JCNQAfZ*j_lwuYx>Z4NzZNwDp3>U7v8^p91a$8~_{y93r4AMqZ^OYD2mHne#Grb)f!X zz!BxTqxq$nRsbu_an_%{hyth^2%mXoe`+WyTPREJ0E7T6>Z_3Y7V;h#JES&8(C=ua zn+b2bM>il=0;`b?C{7dkIHp9bipJ8?heA8HV&(fs$7dN@(vqczRyAD+%e$1#zt0Or zmBV@&XQ?Q(AzemQ5Qa!t+Tw_^>u9OCL#cSxt}lTg%7WqLuO2fc zRuebMO~12;>;8Dcni&=DPSgo0bKaO6Bl~|z*xyF3abg|aL7PB#DoMp_qA@`wn zU)b{ILbItlK|OU$)FJ1H-#RLBhNx1 zV6aF2lG11F3%r3|4;?=uM*`Z*HzGRzFXdP7*|UE|Rb1m@7CMKdW0k+XmtA8lcg@o6 z-r(t$EDrV)>>XfsT5_lK_w}}I=w~lNHT~O2;3-P&$?_~X2v@RZ8`{CVbhA;e>^_-q zeTqutBg#)sPS@>>CA&ZT*2ygr}L?m@-j*qH>=9*kFN>6L@iISFMk;+jwoJ1Z@(vSrN%hJxT%)*dtDu_~J)c}AMD;y?Ic*LdK{wDU zJvY3CH2mt>n!v`&URg`kFJL;_+(G#?8eq4Jx7Fdwkw>YXmdv*`LmJYz@kVmh`dtET z%f^hfB>e^}pmPKk(4tAx#=Ub2wmJBcoUL_JCaCFpy4JyAqDuitlWA z#a8)ZVx?sLnv}ctj|;Cm^R9`V1_@yrwcyjRYwTI(h;F$&I_HU~&T@!T9XmrzT>h1S zP9dH^JDH`o#~ai)x<=}aLKvR)`y!z{qRn^Ega%b0Q-e zp``}B^eQ3|P0F{_UkXt%p10rosY%rT2i)ECeCJY=OrnHOvIk;gF@#}q3&!BH2#03T zSU0*ausPxk65X`5Y}|cv_iebSBuz({;n)pf8=VzHX!8LEp!lFTL7)E5Kz{^iqvGKw zMD=--aEMO^KQxKc=0+!)8&lRhg4qsawPIe5MI4+ura1IqRDMWhO?ni42P z68UQokIGY2FP^(t=KfCQBQQrzwK36Y>!C3nQ~!Zs)%(-LD)oyb@r*b<_*AmE*OcWP ziG6jCO%xTzbD9?P_YU3BLtjtW!~{Oh42zxrzxq-u(?ns+({7w%VyAv)>mns5qnT75oUd}`$&bD8>VhA6Cqf|}^G%tuf2(7BW1SGEH)#W04ugg{3@pm;qW zI%*`Uu2G^_R1e-hO1zSk&wJ2U)aQVuWXYV$O36Xc+hFr+piJs5njmQ2@W8?nCl;#0E*{E}YxHH$asp=%#rE9@e5k5~gW z^R{I;fQY}N0@o;g-dOmWEq0#l&2Lq=LPN>&3=JB^pTje!N*|ze5GCG4@`i*boNsjN>qE|N_43-SG zQvo(W8UPpLG8x;q%O)$;?Ij{_IkP+xw_{P+7(8I3RztF)Ulx~^8M!l~0IQGhb7kJ( z@e=V5+SUjER4N`4*0=SE|Ij5?TF0QrvFax-QQhDI6NxcbL(D#moO1Pws9g-zO&|y$ z=EwN@z$rXp&_nBCqADkgFvm&qgTb;gk!7;6ik#@+Q+4NbQ84J8B3>}fitZ@4>%+&? zqYN#LJ_Ml)&35`KL3YQCsP3bCHDNS_c%kEjfh*ck-EsA1Z0qOIU;RA5by#FD( zfW%4U>Zmy)i})&;Bdn&#Q@lE@Moi3fLda1@8r04jQCbk)YE6=BaEHWy0{3>fM&yW@ zgHP3nd1h-J#&@d8wW5H&U!?Q}YI!Z~wY6$vt*Gq)KPz-8Y^SkElFT7bdo7h|qq`{3 z-{zJSb3|Sicx@~i7##`q@uH~T|5J3wjidac2RU0kRV!u{RYAVG$c8?+L1}bdRj3Vh z!m&6yxrp$%6ZX`9M1y$YfyvB{$gyK+FO_o#AFLCTt*%iR6$}kz4?TafH+S^9C4F9k zJFbXSEK9_Np<2{TUkZQk;Dg>A#|l|TnH(%i-tS5UjhM93x0&`h-+(Pf3~~|`Nf&?x z0g19~Dd^x?z!_^v?jZHw8`Pcnu!`yvOGLg{q`tXCJU4+$_~dZ_*Fb+jTOngNQF%;# zVX1K2xF)OY9d&NKm?T_kN4>Z>#Q9EeuNQ(HT?3J~tT@lcF(CYxX3+yooE+sG5}{^w z_tLqpgWn7B&78;6tM7eLbwwU?S>mj&dh7>U2HnCAy(4Yx>z0+^f~7GLDc+y8*+4d1KX^xjNKX_?)FaKpS;XSSc0g`CDgw@h|64?6|539jv$EwCsm|rX z(WFn`Dls!HolKgdkjRNK#uTHIGR3e(a^M99bZeo97%5%>4?8c~#$f~E)-0egx%rQBZcw~mT@weFDctDE3 z)x^0_a$JRID)N1qzEi5|22mI~r!zRhDtiuZa0EOP9)8$by&La0SIYGohCjT`y)dOnU>E7b}i0 z9Wsg3(qX6ABqTj41@!OQqE0hWzf3@NEfFF8s$AEDn;)*!Te73 zxgFxa6L(OWznvR={7P}NDEwvtm#V%HdAInPB*)8H=RfiZEAtU2XtNA~i4fR$lc;m6dm2kT zqfwJ2j%XbE;K(rez}cK>xop;3>B|`R29kVi<$c6&2l#!Sj1v5o===`$Un5S5Q7+Ns zGBrsD)pBi;ZF`?K%~q#;PvnGbkO6`tnGWEMmHiSR57r_Hp`b#80msTV*zg6uQ7~57H+Hfso8Wvs%>S+wJin?*??C zriCu&DWIN?jM;$ofNp>U>;_y7xCU?|-~iwd;6A_ufQJAN10DnX8gP^#q<;?jThK28 zUIrWkWP_(dKowvHU=d(7U_F3cPd`!;fJ>Pa8Y${GpJ!L8t6?`53znDNHEC2ui diff --git a/Machine-Backend/app/routes/__pycache__/routes.cpython-314.pyc b/Machine-Backend/app/routes/__pycache__/routes.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afff4f39d6fc7a783519da79f11b79b9be7c0ebc GIT binary patch literal 155334 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%0D}MqwHVZ4up2Yu=}-(3ZA=3OjToR)B4!N) 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 z�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 literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/routes/routes.py b/Machine-Backend/app/routes/routes.py index 138e3e9..28d01da 100644 --- a/Machine-Backend/app/routes/routes.py +++ b/Machine-Backend/app/routes/routes.py @@ -1,7 +1,7 @@ from app import db # Add this if not already there from sqlalchemy import func, extract # Add this from flask import Blueprint, request, jsonify, send_from_directory -from app.services.services import MachineService, UserService, ProductService, serial_service, TransactionService, RoleService +from app.services.services import MachineService, UserService, ProductService, serial_service, TransactionService, RoleService, BranchService, BrandService, CategoryService, SubCategoryService from app.models.models import Machine, User, Transaction, Product, VendingSlot, Role import os import hashlib @@ -156,8 +156,12 @@ def add_machine(): return jsonify({"error": "client_id is required"}), 400 print(f"✓ Admin creating machine for client_id: {data['client_id']}") + # ADD CREATED_BY + data['created_by'] = current_user.id + new_machine = MachineService.create_machine(data) print(f"✓ Machine created: {new_machine.machine_id} for client_id: {new_machine.client_id}") + print(f"✓ Created by: {current_user.username} (ID: {current_user.id})") return jsonify(new_machine.to_dict()), 201 @@ -187,6 +191,383 @@ def delete_machine(id): except Exception as e: return jsonify({"error": str(e)}), 500 +@bp.route('/refillers//machines', methods=['GET']) +def get_refiller_machines(refiller_id): + """Get all machines assigned to a Refiller""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Check permissions + is_admin = current_user.roles in ['Management', 'SuperAdmin', 'Admin'] + is_own_data = current_user.id == refiller_id + is_client_checking_own_refiller = False + + if current_user.roles == 'Client': + # Client can view their assigned Refiller's machines + refiller = User.query.get(refiller_id) + if refiller and refiller.assigned_to == current_user.id: + is_client_checking_own_refiller = True + + if not (is_admin or is_own_data or is_client_checking_own_refiller): + return jsonify({'error': 'Permission denied'}), 403 + + # Get machines + machines = UserService.get_refiller_machines(refiller_id) + + return jsonify([machine.to_dict() for machine in machines]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/refillers//machines', methods=['POST']) +def assign_machines_to_refiller(refiller_id): + """Assign machines to a Refiller""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only admins can assign machines + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + data = request.json + machine_ids = data.get('machine_ids', []) + + if not machine_ids: + return jsonify({'error': 'machine_ids array required'}), 400 + + # Assign machines + assignments = UserService.assign_machines_to_refiller( + refiller_id, + machine_ids, + current_user.id + ) + + return jsonify({ + 'message': f'Successfully assigned {len(assignments)} machines', + 'assignments': [a.to_dict() for a in assignments] + }), 200 + + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/refillers//machines', methods=['PUT']) +def update_refiller_machines(refiller_id): + """Update all machine assignments for a Refiller (replaces existing)""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only admins can update assignments + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + data = request.json + machine_ids = data.get('machine_ids', []) + + # Update machines (replaces all existing) + assignments = UserService.update_refiller_machines( + refiller_id, + machine_ids, + current_user.id + ) + + return jsonify({ + 'message': f'Successfully updated machine assignments', + 'assignments': [a.to_dict() for a in assignments], + 'count': len(assignments) + }), 200 + + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/refillers//machines/', methods=['DELETE']) +def remove_machine_from_refiller(refiller_id, machine_id): + """Remove a machine assignment from a Refiller""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only admins can remove assignments + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + success = UserService.remove_machine_from_refiller(refiller_id, machine_id) + + if success: + return jsonify({ + 'message': f'Successfully removed machine {machine_id} from refiller' + }), 200 + else: + return jsonify({'error': 'Assignment not found'}), 404 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +# ============================================================================ +# MACHINE-CENTRIC ENDPOINTS +# ============================================================================ + +@bp.route('/machines//refillers', methods=['GET']) +def get_machine_refillers(machine_id): + """Get all Refillers assigned to a Machine""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Admins and machine owner can view + machine = Machine.query.filter_by(machine_id=machine_id).first() + if not machine: + return jsonify({'error': 'Machine not found'}), 404 + + is_admin = current_user.roles in ['Management', 'SuperAdmin', 'Admin'] + is_owner = machine.client_id == current_user.id + + if not (is_admin or is_owner): + return jsonify({'error': 'Permission denied'}), 403 + + # Get refillers + refillers = MachineService.get_machine_refillers(machine_id) + + return jsonify([refiller.to_dict() for refiller in refillers]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/machines//refillers', methods=['POST']) +def assign_refillers_to_machine(machine_id): + """Assign Refillers to a Machine""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only admins can assign refillers to machines + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + data = request.json + refiller_ids = data.get('refiller_ids', []) + + if not refiller_ids: + return jsonify({'error': 'refiller_ids array required'}), 400 + + # Assign refillers + assignments = MachineService.assign_refillers_to_machine( + machine_id, + refiller_ids, + current_user.id + ) + + return jsonify({ + 'message': f'Successfully assigned {len(assignments)} refillers', + 'assignments': [a.to_dict() for a in assignments] + }), 200 + + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +# ============================================================================ +# UTILITY ENDPOINTS +# ============================================================================ + +@bp.route('/clients//machines', methods=['GET']) +def get_client_machines(client_id): + """Get all machines belonging to a Client (for assignment dropdown)""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Admins or the client themselves can view + is_admin = current_user.roles in ['Management', 'SuperAdmin', 'Admin'] + is_own_data = current_user.id == client_id + + if not (is_admin or is_own_data): + return jsonify({'error': 'Permission denied'}), 403 + + # Get client's machines + machines = Machine.query.filter_by(client_id=client_id).all() + + return jsonify([{ + 'id': m.id, + 'machine_id': m.machine_id, + 'machine_model': m.machine_model, + 'machine_type': m.machine_type, + 'branch_name': m.branch_name, + 'operation_status': m.operation_status + } for m in machines]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/refillers//available-machines', methods=['GET']) +def get_available_machines_for_refiller(refiller_id): + """Get machines available to assign to a Refiller (from their assigned Client)""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only admins can view + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + # Get refiller + refiller = User.query.get_or_404(refiller_id) + + if refiller.roles != 'Refiller': + return jsonify({'error': 'User is not a Refiller'}), 400 + + # If refiller is assigned to a client, get that client's machines + # Otherwise, get all machines + if refiller.assigned_to: + machines = Machine.query.filter_by(client_id=refiller.assigned_to).all() + message = f"Machines from assigned client" + else: + machines = Machine.query.all() + message = "All machines (no client assigned)" + + return jsonify({ + 'message': message, + 'machines': [{ + 'id': m.id, + 'machine_id': m.machine_id, + 'machine_model': m.machine_model, + 'machine_type': m.machine_type, + 'branch_name': m.branch_name, + 'client_name': m.client.username if m.client else m.client_name, + 'operation_status': m.operation_status + } for m in machines] + }), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + +@bp.route('/clients', methods=['GET']) +def get_clients(): + """Get all clients for assignment dropdown""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Management, SuperAdmin, and Admin can see clients + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + + # Get all users with Client role + clients = User.query.filter_by(roles='Client', user_status='Active').all() + + client_list = [{ + 'id': client.id, + 'user_id': client.user_id, + 'username': client.username, + 'email': client.email, + 'contact': client.contact + } for client in clients] + + print(f"✓ Retrieved {len(client_list)} active clients") + + return jsonify(client_list), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/users//assigned-refillers', methods=['GET']) +def get_assigned_refillers(user_id): + """Get all refillers assigned to a specific client""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Check permissions + is_admin = current_user.roles in ['Management', 'SuperAdmin', 'Admin'] + is_own_data = current_user.id == user_id + + if not (is_admin or is_own_data): + return jsonify({'error': 'Permission denied'}), 403 + + # Get user and validate it's a Client + user = User.query.get_or_404(user_id) + if user.roles != 'Client': + return jsonify({'error': 'User is not a Client'}), 400 + + # Get all refillers assigned to this client + refillers = User.query.filter_by( + assigned_to=user_id, + roles='Refiller', + user_status='Active' + ).all() + + refiller_list = [{ + 'id': refiller.id, + 'user_id': refiller.user_id, + 'username': refiller.username, + 'email': refiller.email, + 'contact': refiller.contact, + 'created_by_username': refiller.creator.username if refiller.creator else None + } for refiller in refillers] + + print(f"✓ Client {user.username} has {len(refiller_list)} assigned refillers") + + return jsonify(refiller_list), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + # Updated User Routes in routes.py # In your routes.py file @@ -205,9 +586,30 @@ def add_users(): print("CREATE USER REQUEST") print("=" * 60) + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({"error": "Authentication required"}), 401 + try: # Get form data data = request.form.to_dict() + + # ADD CREATED_BY + data['created_by'] = current_user.id + + # ⭐ NEW: Handle assigned_to + if 'assigned_to' in data: + assigned_to = data.get('assigned_to') + if assigned_to: + try: + data['assigned_to'] = int(assigned_to) + except (ValueError, TypeError): + return jsonify({"error": "Invalid assigned_to value"}), 400 + else: + data['assigned_to'] = None + print("Form data keys:", list(data.keys())) print("Form values:", data) @@ -221,10 +623,6 @@ def add_users(): print(f" Photo: {photo_file.filename if photo_file else None}") print(f" Logo: {logo_file.filename if logo_file else None}") print(f" Documents: {len(document_files)} files") - if document_files: - for i, f in enumerate(document_files): - print(f" {i+1}. {f.filename}") - print(f" Metadata: {documents_metadata}") # Call service print("\nCalling UserService.create_user...") @@ -236,7 +634,9 @@ def add_users(): documents_metadata ) - print("✓ User created successfully!") + print(f"✓ User created successfully by: {current_user.username}") + if new_user.assigned_to: + print(f"✓ Assigned to Client ID: {new_user.assigned_to}") print("=" * 60 + "\n") return jsonify(new_user.to_dict()), 201 @@ -258,27 +658,40 @@ def add_users(): return jsonify({"error": f"Server error: {str(e)}"}), 500 + +# UPDATE the existing update_user endpoint to handle assigned_to @bp.route('/users/', methods=['PUT']) def update_user(id): try: data = request.form.to_dict() + # ⭐ NEW: Handle assigned_to + if 'assigned_to' in data: + assigned_to = data.get('assigned_to') + if assigned_to: + try: + data['assigned_to'] = int(assigned_to) + except (ValueError, TypeError): + return jsonify({"error": "Invalid assigned_to value"}), 400 + else: + data['assigned_to'] = None + # Get uploaded files photo_file = request.files.get('photo') logo_file = request.files.get('company_logo') document_files = request.files.getlist('documents') - # Get document metadata - THIS IS NEW + # Get document metadata documents_metadata = request.form.get('documents_metadata') - # Pass metadata to service - UPDATED PARAMETER LIST + # Pass metadata to service updated_user = UserService.update_user( id, data, photo_file, logo_file, document_files, - documents_metadata # NEW PARAMETER + documents_metadata ) return jsonify(updated_user.to_dict()) except ValueError as e: @@ -286,6 +699,8 @@ def update_user(id): except Exception as e: return jsonify({"error": str(e)}), 500 + + @bp.route('/users/', methods=['DELETE']) def delete_user(id): try: @@ -334,10 +749,23 @@ def get_products(): @bp.route('/products', methods=['POST']) def add_product(): + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({"error": "Authentication required"}), 401 + try: - data = request.form + data = request.form.to_dict() + + # ADD CREATED_BY + data['created_by'] = current_user.id + file = request.files.get('product_image') new_product = ProductService.create_product(data, file) + + print(f"✓ Product created by: {current_user.username}") + return jsonify(new_product.to_dict()), 201 except ValueError as e: return jsonify({"error": str(e)}), 400 @@ -852,7 +1280,6 @@ def get_current_user_from_token(): except: return None - from flask import jsonify, request from sqlalchemy import func, extract from datetime import datetime @@ -2080,18 +2507,26 @@ def get_roles(): @bp.route('/roles', methods=['POST']) def create_role(): """Create new role""" + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Management and SuperAdmin can create roles + if current_user.roles not in ['Management', 'SuperAdmin', 'Admin']: + return jsonify({'error': 'Permission denied'}), 403 + try: - current_user = get_current_user_from_token() - if not current_user: - return jsonify({'error': 'Authentication required'}), 401 - - # Only Management and SuperAdmin can create roles - if current_user.roles not in ['Management', 'SuperAdmin','Admin']: - return jsonify({'error': 'Permission denied'}), 403 - data = request.json + + # ADD CREATED_BY + data['created_by'] = current_user.id + new_role = RoleService.create_role(data) + print(f"✓ Role created by: {current_user.username}") + return jsonify(new_role.to_dict()), 201 except ValueError as e: @@ -2185,4 +2620,750 @@ def get_available_permissions(): return jsonify(permissions), 200 except Exception as e: - return jsonify({'error': str(e)}), 500 \ No newline at end of file + return jsonify({'error': str(e)}), 500 + +@bp.route('/branches', methods=['GET']) +def get_branches(): + """Get all branches""" + try: + # Get current user from token + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import BranchService + branches = BranchService.get_all_branches() + + return jsonify([branch.to_dict() for branch in branches]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/branches/', methods=['GET']) +def get_branch(branch_id): + """Get single branch by ID""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import BranchService + branch = BranchService.get_branch_by_id(branch_id) + + return jsonify(branch.to_dict()), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 404 + + +@bp.route('/branches', methods=['POST']) +def create_branch(): + print("\n" + "=" * 60) + print("CREATE BRANCH REQUEST") + print("=" * 60) + + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can create branches + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + try: + data = request.json + + # ADD CREATED_BY + data['created_by'] = current_user.id + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + + new_branch = BranchService.create_branch(data) + + print(f"✓ Branch created successfully by: {current_user.username}") + print("=" * 60 + "\n") + + return jsonify(new_branch.to_dict()), 201 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + +@bp.route('/branches/', methods=['PUT']) +def update_branch(branch_id): + """Update existing branch""" + print("\n" + "=" * 60) + print(f"UPDATE BRANCH REQUEST - ID: {branch_id}") + print("=" * 60) + + try: + # Get current user from token + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can update branches + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + data = request.json + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + + from app.services.services import BranchService + updated_branch = BranchService.update_branch(branch_id, data) + + print(f"✓ Branch updated successfully: {updated_branch.branch_id}") + print("=" * 60 + "\n") + + return jsonify(updated_branch.to_dict()), 200 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + + +@bp.route('/branches/', methods=['DELETE']) +def delete_branch(branch_id): + """Delete branch""" + print("\n" + "=" * 60) + print(f"DELETE BRANCH REQUEST - ID: {branch_id}") + print("=" * 60) + + try: + # Get current user from token + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can delete branches + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + print(f"User: {current_user.username} ({current_user.roles})") + + from app.services.services import BranchService + BranchService.delete_branch(branch_id) + + print(f"✓ Branch deleted successfully") + print("=" * 60 + "\n") + + return jsonify({'message': 'Branch deleted successfully'}), 200 + + except Exception as e: + print(f"\n✗ ERROR: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 500 + + +@bp.route('/branches/search', methods=['GET']) +def search_branches(): + """Search branches by query""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + query = request.args.get('q', '') + + if not query: + return jsonify({'error': 'Search query required'}), 400 + + from app.services.services import BranchService + branches = BranchService.search_branches(query) + + return jsonify([branch.to_dict() for branch in branches]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + +# Brand Management Routes +@bp.route('/brands', methods=['GET']) +def get_brands(): + """Get all brands""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import BrandService + brands = BrandService.get_all_brands() + + return jsonify([brand.to_dict() for brand in brands]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/brands/', methods=['GET']) +def get_brand(brand_id): + """Get single brand by ID""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import BrandService + brand = BrandService.get_brand_by_id(brand_id) + + return jsonify(brand.to_dict()), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 404 + + +@bp.route('/brands', methods=['POST']) +def create_brand(): + print("\n" + "=" * 60) + print("CREATE BRAND REQUEST") + print("=" * 60) + + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can create brands + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + try: + # Get form data + data = request.form.to_dict() + + # ADD CREATED_BY + data['created_by'] = current_user.id + + # Get image file + image_file = request.files.get('image') + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + print(f"Image: {image_file.filename if image_file else 'None'}") + + new_brand = BrandService.create_brand(data, image_file) + + print(f"✓ Brand created successfully by: {current_user.username}") + print("=" * 60 + "\n") + + return jsonify(new_brand.to_dict()), 201 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + +@bp.route('/brands/', methods=['PUT']) +def update_brand(brand_id): + """Update existing brand""" + print("\n" + "=" * 60) + print(f"UPDATE BRAND REQUEST - ID: {brand_id}") + print("=" * 60) + + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can update brands + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + # Get form data + data = request.form.to_dict() + + # Get image file if provided + image_file = request.files.get('image') if 'image' in request.files else None + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + print(f"Image: {image_file.filename if image_file else 'No change'}") + + from app.services.services import BrandService + updated_brand = BrandService.update_brand(brand_id, data, image_file) + + print(f"✓ Brand updated successfully: {updated_brand.brand_id}") + print("=" * 60 + "\n") + + return jsonify(updated_brand.to_dict()), 200 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + + +@bp.route('/brands/', methods=['DELETE']) +def delete_brand(brand_id): + """Delete brand""" + print("\n" + "=" * 60) + print(f"DELETE BRAND REQUEST - ID: {brand_id}") + print("=" * 60) + + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can delete brands + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + print(f"User: {current_user.username} ({current_user.roles})") + + from app.services.services import BrandService + BrandService.delete_brand(brand_id) + + print(f"✓ Brand deleted successfully") + print("=" * 60 + "\n") + + return jsonify({'message': 'Brand deleted successfully'}), 200 + + except Exception as e: + print(f"\n✗ ERROR: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 500 + + +@bp.route('/brands/search', methods=['GET']) +def search_brands(): + """Search brands by query""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + query = request.args.get('q', '') + + if not query: + return jsonify({'error': 'Search query required'}), 400 + + from app.services.services import BrandService + brands = BrandService.search_brands(query) + + return jsonify([brand.to_dict() for brand in brands]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@bp.route('/brands/by-branch/', methods=['GET']) +def get_brands_by_branch(branch_id): + """Get all brands for a specific branch""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import BrandService + brands = BrandService.get_brands_by_branch(branch_id) + + return jsonify([brand.to_dict() for brand in brands]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +# Serve brand images +@bp.route('/uploads/brand_images/') +def serve_brand_image(filename): + return send_from_directory(os.path.join(bp.root_path, '..', 'Uploads', 'brand_images'), filename) + + +# Category Management Routes +@bp.route('/categories', methods=['GET']) +def get_categories(): + """Get all categories""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import CategoryService + categories = CategoryService.get_all_categories() + + return jsonify([category.to_dict() for category in categories]), 200 + + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + + +@bp.route('/categories/', methods=['GET']) +def get_category(category_id): + """Get single category by ID""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import CategoryService + category = CategoryService.get_category_by_id(category_id) + + return jsonify(category.to_dict()), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 404 + + +@bp.route('/categories', methods=['POST']) +def create_category(): + print("\n" + "=" * 60) + print("CREATE CATEGORY REQUEST") + print("=" * 60) + + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can create categories + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + try: + # Get form data + data = request.form.to_dict() + + # ADD CREATED_BY + data['created_by'] = current_user.id + + # Get image file + image_file = request.files.get('image') + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + + new_category = CategoryService.create_category(data, image_file) + + print(f"✓ Category created successfully by: {current_user.username}") + print("=" * 60 + "\n") + + return jsonify(new_category.to_dict()), 201 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + +@bp.route('/categories/', methods=['PUT']) +def update_category(category_id): + """Update existing category""" + print("\n" + "=" * 60) + print(f"UPDATE CATEGORY REQUEST - ID: {category_id}") + print("=" * 60) + + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can update categories + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + # Get form data + data = request.form.to_dict() + + # Get image file if provided + image_file = request.files.get('image') if 'image' in request.files else None + + print(f"User: {current_user.username} ({current_user.roles})") + print(f"Data: {data}") + print(f"Image: {image_file.filename if image_file else 'No change'}") + + from app.services.services import CategoryService + updated_category = CategoryService.update_category(category_id, data, image_file) + + print(f"✓ Category updated successfully: {updated_category.category_id}") + print("=" * 60 + "\n") + + return jsonify(updated_category.to_dict()), 200 + + except ValueError as e: + print(f"\n✗ Validation Error: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 400 + + except Exception as e: + print(f"\n✗ SERVER ERROR") + print(f"Error type: {type(e).__name__}") + print(f"Error message: {str(e)}") + + import traceback + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60 + "\n") + + return jsonify({'error': f'Server error: {str(e)}'}), 500 + + +@bp.route('/categories/', methods=['DELETE']) +def delete_category(category_id): + """Delete category""" + print("\n" + "=" * 60) + print(f"DELETE CATEGORY REQUEST - ID: {category_id}") + print("=" * 60) + + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + # Only Admin, Management, and SuperAdmin can delete categories + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + print(f"User: {current_user.username} ({current_user.roles})") + + from app.services.services import CategoryService + CategoryService.delete_category(category_id) + + print(f"✓ Category deleted successfully") + print("=" * 60 + "\n") + + return jsonify({'message': 'Category deleted successfully'}), 200 + + except Exception as e: + print(f"\n✗ ERROR: {str(e)}") + print("=" * 60 + "\n") + return jsonify({'error': str(e)}), 500 + + +@bp.route('/categories/search', methods=['GET']) +def search_categories(): + """Search categories by query""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + query = request.args.get('q', '') + + if not query: + return jsonify({'error': 'Search query required'}), 400 + + from app.services.services import CategoryService + categories = CategoryService.search_categories(query) + + return jsonify([category.to_dict() for category in categories]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@bp.route('/categories/by-brand/', methods=['GET']) +def get_categories_by_brand(brand_id): + """Get all categories for a specific brand""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import CategoryService + categories = CategoryService.get_categories_by_brand(brand_id) + + return jsonify([category.to_dict() for category in categories]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@bp.route('/categories/by-branch/', methods=['GET']) +def get_categories_by_branch(branch_id): + """Get all categories for a specific branch""" + try: + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + from app.services.services import CategoryService + categories = CategoryService.get_categories_by_branch(branch_id) + + return jsonify([category.to_dict() for category in categories]), 200 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +# Serve category images +@bp.route('/uploads/category_images/') +def serve_category_image(filename): + return send_from_directory(os.path.join(bp.root_path, '..', 'Uploads', 'category_images'), filename) + +@bp.route('/subcategories', methods=['GET']) +def get_subcategories(): + try: + current_user = get_current_user_from_token() + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + from app.services.services import SubCategoryService + return jsonify([s.to_dict() for s in SubCategoryService.get_all()]), 200 + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@bp.route('/subcategories/', methods=['GET']) +def get_subcategory(id): + try: + current_user = get_current_user_from_token() + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + from app.services.services import SubCategoryService + return jsonify(SubCategoryService.get_by_id(id).to_dict()), 200 + except Exception as e: + return jsonify({'error': str(e)}), 404 + +@bp.route('/subcategories', methods=['POST']) +def create_subcategory(): + # Get current user + current_user = get_current_user_from_token() + + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + if current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + + try: + data = request.form.to_dict() + + # ADD CREATED_BY + data['created_by'] = current_user.id + + image = request.files.get('image') + + new_sub = SubCategoryService.create(data, image) + + print(f"✓ SubCategory created by: {current_user.username}") + + return jsonify(new_sub.to_dict()), 201 + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + +@bp.route('/subcategories/', methods=['PUT']) +def update_subcategory(id): + try: + current_user = get_current_user_from_token() + if not current_user or current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + data = request.form.to_dict() + image = request.files.get('image') if 'image' in request.files else None + from app.services.services import SubCategoryService + updated = SubCategoryService.update(id, data, image) + return jsonify(updated.to_dict()), 200 + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@bp.route('/subcategories/', methods=['DELETE']) +def delete_subcategory(id): + try: + current_user = get_current_user_from_token() + if not current_user or current_user.roles not in ['Admin', 'Management', 'SuperAdmin']: + return jsonify({'error': 'Permission denied'}), 403 + from app.services.services import SubCategoryService + SubCategoryService.delete(id) + return jsonify({'message': 'SubCategory deleted'}), 200 + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@bp.route('/uploads/subcategory_images/') +def serve_subcategory_image(filename): + return send_from_directory(os.path.join(bp.root_path, '..', 'Uploads', 'subcategory_images'), filename) \ No newline at end of file diff --git a/Machine-Backend/app/services/__pycache__/__init__.cpython-314.pyc b/Machine-Backend/app/services/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb2252d4f6593f29db04c819abffaf920771f910 GIT binary patch literal 259 zcmdPq(j9^HV-N=hn4pZ$Qb0xnLmXoeqbGw0V+o@NQwfs?a|yFNLlBcP zgC_G!MxX*s=39KeiOCt6d8xsvMP-@EskgX8i&KkW9KL{}{FKt<5-8tKljRl%P#UDL zxQH2OKoJXwU7_Y|MY=`?1_m)uJ9M2Ale1IvQeqMd z3Swa9#KgyE=4F<|$LkeT-r}&y1zMPsYF7mEBFKrw+(6<3Gb1D8eFn$N430${Kt2Fm CH$+DO literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/services/__pycache__/services.cpython-313.pyc b/Machine-Backend/app/services/__pycache__/services.cpython-313.pyc index e7ccb533a5be2d11a84495bd4b84651476fc638c..7ae7e94399820bfdf3614936ae98d0aee40455a1 100644 GIT binary patch delta 30381 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

branch-list works!

+
+ +
+ + @if (isLoading) { +
+ +
+ } + +
Branch Management
+ +
+ + + + + + + +
+
+ + +
+ +
+ @if (branches$ | async; as branches) { + @if (branches.length > 0 || selectedBranch) { +
+ +
+ +
Code
+
Branch Name
+ + + + +
Actions
+
+ + + @if (selectedBranch && !selectedBranch.id) { +
+ + + + +
+
New Branch
+
+ + + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+ } + + + @for (branch of branches; track trackByFn($index, branch)) { +
+ + + + +
+
+
{{ branch.code }}
+
{{ branch.branch_id }}
+
+
+ + +
+
{{ branch.name }}
+
+ + + + + + + + + + + +
+ + + +
+
+ + + @if (selectedBranch?.id === branch.id) { +
+ +
+ } + } +
+ + + } @else { + +
+
+ +
+

No branches found

+

Get started by creating your first branch

+ +
+ } + } + + + +
+
+
+
+ +
+

+ {{ branch.id ? 'Edit Branch' : 'Create New Branch' }} +

+ @if (branch.branch_id) { +

Branch ID: {{ branch.branch_id }}

+ } +
+ + +
+ + + Branch Code + + + Code is required + + + Use uppercase letters and numbers only + + Unique branch identifier + + + + + Branch Name + + + Name is required + + + Minimum 3 characters required + + + + + + Location + + + Location is required + + + + + + Contact Number + + + Contact is required + + + Enter valid 10-digit number + + + + + + Address + + + Address is required + + + Minimum 10 characters required + + + + + @if (branch.id && branch.created_by_username) { + + Created By + + + + } +
+ + + @if (branch.id) { +
+
+
+ Created: + {{ branch.created_at | date:'medium' }} +
+ @if (branch.updated_at && branch.updated_at !== branch.created_at) { +
+ Last Updated: + {{ branch.updated_at | date:'medium' }} +
+ } +
+
+ } +
+ + +
+ +
+ @if (flashMessage) { +
+ @if (flashMessage === 'success') { + + {{ branch.id ? 'Updated' : 'Created' }} successfully + } + @if (flashMessage === 'error') { + + Error occurred + } +
+ } + + +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.scss b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.scss index e69de29..1d67766 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.scss +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.scss @@ -0,0 +1,336 @@ +/* Branch grid layout - NOW 7 COLUMNS WITH CREATED_BY */ +.branch-grid { + // Branch ID, Code, Name, Location, Contact, Created By, Actions (7 columns) + grid-template-columns: 110px 120px 1fr 150px 130px 150px 120px; + + @media (max-width: 1279px) { // xl breakpoint - hide Created By + grid-template-columns: 110px 120px 1fr 150px 130px 120px; + } + + @media (max-width: 1023px) { // lg breakpoint - hide location and contact + grid-template-columns: 110px 120px 1fr 120px; + } + + @media (max-width: 767px) { // md breakpoint - hide branch ID + grid-template-columns: 120px 1fr 120px; + } + + @media (max-width: 639px) { // sm breakpoint - stack vertically + grid-template-columns: 1fr 80px; + } +} + +/* Created By Column Styling */ +.branch-grid > div:nth-child(6) { + display: flex; + align-items: center; + gap: 6px; +} + +.branch-grid > div:nth-child(6) mat-icon { + color: #6b7280; + font-size: 16px; + width: 16px; + height: 16px; +} + +.branch-grid > div:nth-child(6) span { + font-size: 13px; + color: #374151; +} + +.dark .branch-grid > div:nth-child(6) mat-icon { + color: #9ca3af; +} + +.dark .branch-grid > div:nth-child(6) span { + color: #d1d5db; +} + +/* Material form field customizations */ +.fuse-mat-dense { + .mat-mdc-form-field-subscript-wrapper { + display: none; + } + + .mat-mdc-form-field-infix { + min-height: 40px; + } +} + +.fuse-mat-rounded { + .mat-mdc-form-field-flex { + border-radius: 20px; + background-color: rgba(0, 0, 0, 0.04); + border: none; + } + + &.mat-focused .mat-mdc-form-field-flex { + background-color: rgba(0, 0, 0, 0.06); + } +} + +/* Icon size utilities */ +.icon-size-5 { + width: 20px; + height: 20px; + font-size: 20px; +} + +.icon-size-16 { + width: 64px; + height: 64px; + font-size: 64px; +} + +/* Minimal button styles */ +mat-icon-button { + width: 36px; + height: 36px; + + mat-icon { + transition: all 0.2s ease; + } + + &:hover mat-icon { + transform: scale(1.1); + } +} + +/* Form field minimal styling */ +mat-form-field { + &.mat-mdc-form-field { + width: 100%; + } + + .mat-mdc-text-field-wrapper { + background-color: #ffffff; + } +} + +/* Branch row hover effect */ +.branch-grid > div:not(.sticky):not(.bg-blue-50) { + transition: background-color 0.15s ease; + cursor: pointer; +} + +/* Action buttons minimal styling */ +.mat-mdc-icon-button { + &.mat-primary { + color: #3b82f6; + } + + &.mat-warn { + color: #ef4444; + } +} + +/* Clean header styling */ +.branch-grid.sticky { + letter-spacing: 0.025em; +} + +/* Loading state */ +.animate-spin { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/* Empty state styling */ +.empty-state { + min-height: 400px; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .branch-grid { + gap: 8px; + padding: 12px 16px; + } +} + +/* Dark mode support */ +@media (prefers-color-scheme: dark) { + mat-form-field { + .mat-mdc-text-field-wrapper { + background-color: rgba(255, 255, 255, 0.05); + } + } + + .fuse-mat-rounded .mat-mdc-form-field-flex { + background-color: rgba(255, 255, 255, 0.06); + } + + .fuse-mat-rounded.mat-focused .mat-mdc-form-field-flex { + background-color: rgba(255, 255, 255, 0.08); + } + + .branch-grid > div:hover { + background-color: rgba(255, 255, 255, 0.05) !important; + } +} + +/* Custom scrollbar - minimal */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #d1d5db; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #9ca3af; +} + +/* Focus states for accessibility */ +.mat-mdc-form-field.mat-focused .mat-mdc-form-field-flex { + outline: 2px solid #3b82f6; + outline-offset: 2px; +} + +/* Clean transitions */ +* { + transition-property: background-color, border-color, color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +/* Pagination styling - minimal */ +mat-paginator { + background-color: transparent; +} + +/* Branch ID badge styling */ +.font-mono { + letter-spacing: 0.05em; +} + +/* Clean form appearance */ +.mat-mdc-form-field-appearance-outline { + .mat-mdc-form-field-outline { + color: #e5e7eb; + } + + &.mat-focused .mat-mdc-form-field-outline { + color: #3b82f6; + } +} + +/* Minimal hint text */ +.mat-mdc-form-field-hint { + font-size: 12px; + color: #6b7280; +} + +/* Error state - clean */ +.mat-mdc-form-field-error { + font-size: 12px; +} + +/* Button height consistency */ +.mat-mdc-raised-button, +.mat-mdc-outlined-button { + height: 40px; + line-height: 40px; +} + +/* Clean dividers */ +.border-b { + border-color: #e5e7eb; +} + +.border-t { + border-color: #e5e7eb; +} + +/* Typography - clean hierarchy */ +h2 { + font-weight: 600; + line-height: 1.2; +} + +h3 { + font-weight: 600; + line-height: 1.3; +} + +/* Compact spacing */ +.compact-spacing { + padding: 0.5rem; +} + +/* Minimal shadow on details panel */ +.overflow-hidden { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); +} + +/* Clean action button group */ +.flex.gap-1 { + button { + &:hover { + background-color: rgba(0, 0, 0, 0.04); + } + } +} + +/* Smooth hover transitions */ +.transition-colors { + transition-property: background-color, color; + transition-duration: 200ms; +} + +/* Clean badge for branch ID */ +.text-blue-600 { + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Minimal progress bar */ +mat-progress-bar { + height: 2px; +} + +/* Clean tooltip */ +.mat-mdc-tooltip { + font-size: 12px; + padding: 6px 12px; +} + +/* Responsive typography */ +@media (max-width: 640px) { + .text-4xl { + font-size: 1.875rem; + } +} + +/* Clean focus ring */ +button:focus-visible { + outline: 2px solid #3b82f6; + outline-offset: 2px; +} + +/* Minimal snackbar */ +.snackbar-success { + background-color: #10b981; +} + +.snackbar-error { + background-color: #ef4444; +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.ts index 1f2b6f3..cdd5ed2 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.ts +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.ts @@ -1,12 +1,325 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormControl } from '@angular/forms'; +import { MatTableModule } from '@angular/material/table'; +import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator'; +import { MatSortModule, MatSort } from '@angular/material/sort'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatDialogModule, MatDialog } from '@angular/material/dialog'; +import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; +import { BehaviorSubject, Observable, Subject, debounceTime, distinctUntilChanged, map, switchMap, takeUntil } from 'rxjs'; +import { BranchService, Branch } from './branch.service'; @Component({ selector: 'app-branch-list', standalone: true, - imports: [], + imports: [ + CommonModule, + ReactiveFormsModule, + MatTableModule, + MatPaginatorModule, + MatSortModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatProgressBarModule, + MatDialogModule, + MatSnackBarModule + ], templateUrl: './branch-list.component.html', styleUrl: './branch-list.component.scss' }) -export class BranchListComponent { +export class BranchListComponent implements OnInit, OnDestroy { + @ViewChild(MatPaginator) paginator!: MatPaginator; + @ViewChild(MatSort) sort!: MatSort; -} + // Observables + branches$: Observable; + private branchesSubject = new BehaviorSubject([]); + private destroy$ = new Subject(); + + // Form controls + searchInputControl = new FormControl(''); + selectedBranchForm!: FormGroup; + + // State management + selectedBranch: Branch | null = null; + isLoading = false; + flashMessage: 'success' | 'error' | null = null; + + // Pagination + pagination = { + length: 0, + page: 0, + size: 10 + }; + + constructor( + private fb: FormBuilder, + private branchService: BranchService, + private dialog: MatDialog, + private snackBar: MatSnackBar + ) { + this.branches$ = this.branchesSubject.asObservable(); + this.initializeForm(); + } + + ngOnInit(): void { + this.loadBranches(); + this.setupSearch(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + /** + * Initialize the branch form + */ + private initializeForm(): void { + this.selectedBranchForm = this.fb.group({ + code: ['', [Validators.required, Validators.pattern(/^[A-Z0-9]+$/)]], + name: ['', [Validators.required, Validators.minLength(3)]], + location: ['', [Validators.required]], + address: ['', [Validators.required, Validators.minLength(10)]], + contact: ['', [Validators.required, Validators.pattern(/^[0-9]{10}$/)]] + }); + } + + /** + * Setup search functionality with debounce + */ + private setupSearch(): void { + this.searchInputControl.valueChanges + .pipe( + debounceTime(300), + distinctUntilChanged(), + takeUntil(this.destroy$) + ) + .subscribe((searchTerm) => { + this.filterBranches(searchTerm || ''); + }); + } + + /** + * Load all branches from the backend + */ + loadBranches(): void { + this.isLoading = true; + + this.branchService.getAllBranches() + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (branches) => { + this.branchesSubject.next(branches); + this.pagination.length = branches.length; + this.isLoading = false; + }, + error: (error) => { + console.error('Error loading branches:', error); + this.showSnackBar('Failed to load branches', 'error'); + this.isLoading = false; + } + }); + } + + /** + * Filter branches based on search term + */ + private filterBranches(searchTerm: string): void { + this.branchService.getAllBranches() + .pipe( + map(branches => { + if (!searchTerm) { + return branches; + } + + const term = searchTerm.toLowerCase(); + return branches.filter(branch => + branch.name.toLowerCase().includes(term) || + branch.code.toLowerCase().includes(term) || + branch.location.toLowerCase().includes(term) || + branch.branch_id.toLowerCase().includes(term) + ); + }), + takeUntil(this.destroy$) + ) + .subscribe(filtered => { + this.branchesSubject.next(filtered); + this.pagination.length = filtered.length; + }); + } + + /** + * Create new branch - open form + */ + createBranch(): void { + this.selectedBranch = { + id: 0, + branch_id: '', + code: '', + name: '', + location: '', + address: '', + contact: '', + created_at: '', + updated_at: '' + }; + + this.selectedBranchForm.reset(); + this.flashMessage = null; + } + + /** + * Edit existing branch + */ + editBranch(branch: Branch): void { + this.selectedBranch = { ...branch }; + this.selectedBranchForm.patchValue({ + code: branch.code, + name: branch.name, + location: branch.location, + address: branch.address, + contact: branch.contact + }); + this.flashMessage = null; + } + + /** + * Toggle branch details view + */ + toggleDetails(branchId: number): void { + if (this.selectedBranch?.id === branchId) { + this.closeDetails(); + } else { + const branch = this.branchesSubject.value.find(b => b.id === branchId); + if (branch) { + this.editBranch(branch); + } + } + } + + /** + * Close details panel + */ + closeDetails(): void { + this.selectedBranch = null; + this.selectedBranchForm.reset(); + this.flashMessage = null; + } + + /** + * Update or create branch + */ + updateSelectedBranch(): void { + if (this.selectedBranchForm.invalid) { + this.selectedBranchForm.markAllAsTouched(); + return; + } + + this.isLoading = true; + const formData = this.selectedBranchForm.value; + + const operation = this.selectedBranch?.id + ? this.branchService.updateBranch(this.selectedBranch.id, formData) + : this.branchService.createBranch(formData); + + operation + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (branch) => { + this.flashMessage = 'success'; + this.isLoading = false; + this.showSnackBar( + this.selectedBranch?.id ? 'Branch updated successfully' : 'Branch created successfully', + 'success' + ); + + setTimeout(() => { + this.closeDetails(); + this.loadBranches(); + }, 1500); + }, + error: (error) => { + console.error('Error saving branch:', error); + this.flashMessage = 'error'; + this.isLoading = false; + this.showSnackBar( + error.error?.error || 'Failed to save branch', + 'error' + ); + } + }); + } + + /** + * Delete selected branch + */ + deleteSelectedBranch(): void { + if (!this.selectedBranch?.id) { + return; + } + + if (!confirm(`Are you sure you want to delete branch "${this.selectedBranch.name}"?`)) { + return; + } + + this.deleteBranch(this.selectedBranch); + } + + /** + * Delete branch + */ + deleteBranch(branch: Branch): void { + if (!confirm(`Are you sure you want to delete branch "${branch.name}"?`)) { + return; + } + + this.isLoading = true; + + this.branchService.deleteBranch(branch.id) + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: () => { + this.showSnackBar('Branch deleted successfully', 'success'); + this.closeDetails(); + this.loadBranches(); + }, + error: (error) => { + console.error('Error deleting branch:', error); + this.showSnackBar( + error.error?.error || 'Failed to delete branch', + 'error' + ); + this.isLoading = false; + } + }); + } + + /** + * Track by function for ngFor + */ + trackByFn(index: number, item: Branch): number { + return item.id; + } + + /** + * Show snackbar notification + */ + private showSnackBar(message: string, type: 'success' | 'error'): void { + this.snackBar.open(message, 'Close', { + duration: 3000, + horizontalPosition: 'end', + verticalPosition: 'top', + panelClass: type === 'success' ? 'snackbar-success' : 'snackbar-error' + }); + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch.service.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch.service.ts new file mode 100644 index 0000000..6f2426e --- /dev/null +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { environment } from '../../../../../../environments/environment'; + +export interface Branch { + id: number; + branch_id: string; + code: string; + name: string; + location: string; + address: string; + contact: string; + created_at: string; + updated_at: string; + + // ⭐ NEW: Created by fields (automatic from backend) + created_by?: number; + created_by_username?: string; +} + +export interface BranchCreateRequest { + code: string; + name: string; + location: string; + address: string; + contact: string; + // ⭐ NOTE: created_by is NOT included here + // Backend automatically extracts it from JWT token +} + +export interface BranchUpdateRequest extends BranchCreateRequest {} + +@Injectable({ + providedIn: 'root' +}) +export class BranchService { + private apiUrl = `${environment.apiUrl}/branches`; // Adjust based on your environment setup + + constructor(private http: HttpClient) {} + + /** + * Get authorization headers + */ + private getHeaders(): HttpHeaders { + const token = localStorage.getItem('accessToken'); + return new HttpHeaders({ + 'Content-Type': 'application/json', + 'Authorization': token ? `Bearer ${token}` : '' + }); + } + + /** + * Get all branches + */ + getAllBranches(): Observable { + return this.http.get(this.apiUrl, { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Get single branch by ID + */ + getBranch(id: number): Observable { + return this.http.get(`${this.apiUrl}/${id}`, { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Create new branch + * ⭐ NOTE: Don't send created_by in request + * Backend automatically extracts it from JWT token + */ + createBranch(data: BranchCreateRequest): Observable { + return this.http.post(this.apiUrl, data, { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Update existing branch + * ⭐ NOTE: Don't update created_by + * It's set once during creation and shouldn't change + */ + updateBranch(id: number, data: BranchUpdateRequest): Observable { + return this.http.put(`${this.apiUrl}/${id}`, data, { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Delete branch + */ + deleteBranch(id: number): Observable<{ message: string }> { + return this.http.delete<{ message: string }>(`${this.apiUrl}/${id}`, { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Search branches by query + */ + searchBranches(query: string): Observable { + return this.http.get(`${this.apiUrl}/search?q=${encodeURIComponent(query)}`, + { headers: this.getHeaders() }) + .pipe( + catchError(this.handleError) + ); + } + + /** + * Handle HTTP errors + */ + private handleError(error: any): Observable { + console.error('API Error:', error); + + let errorMessage = 'An error occurred'; + + if (error.error instanceof ErrorEvent) { + // Client-side error + errorMessage = error.error.message; + } else { + // Server-side error + errorMessage = error.error?.error || error.message || 'Server error'; + } + + return throwError(() => ({ + error: errorMessage, + status: error.status + })); + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.html b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.html index a1e43bc..ffd6e8c 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.html +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.html @@ -1 +1,335 @@ -

client-list works!

+ +
+ +
+ + @if (isLoading) { +
+ +
+ } + + +
+
Client Management
+ + {{ pagination.length }} Clients + +
+ + +
+ + + + + + + + +
+
+ + +
+ +
+ @if (clients$ | async; as clients) { + @if (clients.length > 0 || selectedClient) { +
+ +
+ +
Client Name
+ + + + + + +
+ + + @for (client of clients; track trackByFn($index, client)) { +
+ + + + +
+
+ @if (client.photo) { + + } @else { + + {{ client.username.charAt(0) }} + + } +
+
+
{{ client.username }}
+ @if (client.user_id) { +
{{ client.user_id }}
+ } +
+
+ + + + + + + + + + + + + + + + + + +
+ + + @if (selectedClient?.id === client.id) { +
+ +
+ } + } +
+ + + } @else { +
+ No clients found! +
+ } + } + + + +
+
+
+ +
+
+ +
+ @if (client.photo) { + + } @else { + + {{ client.username.charAt(0) }} + + } +
+ + +
+

{{ client.username }}

+

{{ client.user_id }}

+

{{ client.email }}

+

{{ client.contact }}

+
+
+ + + @if (client.company_logo) { +
+ Company Logo +
+ Company logo +
+
+ } +
+ + +
+
+

+ + Machines ({{ client.machines_count }}) +

+
+ + @if (client.machines && client.machines.length > 0) { +
+ @for (machine of client.machines; track machine.machine_id) { +
+
+
+
{{ machine.machine_id }}
+
{{ machine.machine_model }}
+
{{ machine.machine_type }}
+
+ devices +
+ +
+
+ + {{ machine.branch_name }} +
+ + {{ machine.operation_status }} + +
+ +
+ + + {{ machine.connection_status }} + +
+
+ } +
+ } @else { +
+ devices_other +

No machines assigned yet

+
+ } +
+ + +
+
+

+ + Assigned Refillers ({{ client.refillers_count }}) +

+
+ + @if (client.refillers && client.refillers.length > 0) { +
+ @for (refiller of client.refillers; track refiller.id) { +
+
+
+
{{ refiller.username }}
+
{{ refiller.email }}
+
{{ refiller.contact }}
+
{{ refiller.user_id }}
+
+ person +
+ + + @if (refiller.assigned_machines_count > 0) { +
+
+ Assigned Machines ({{ refiller.assigned_machines_count }}) +
+
+ @for (machine of refiller.assigned_machines; track machine.machine_id) { +
+ {{ machine.machine_id }} + {{ machine.machine_model }} +
+ } +
+
+ } @else { +
+ No machines assigned +
+ } +
+ } +
+ } @else { +
+ group +

No refillers assigned yet

+
+ } +
+ + +
+ +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.scss b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.scss index e69de29..bb9e5a2 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.scss +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.scss @@ -0,0 +1,428 @@ +/* client-list.component.scss */ + +/* Client grid layout - 8 COLUMNS: User ID, Name, Email, Contact, Status, Machine ID, Refillers, Details */ +.client-grid { + // Default: All 8 columns visible on XL screens + grid-template-columns: 140px 2fr 2fr 1.5fr 1fr 1.5fr 1.5fr 80px; + + // Ensure all items are vertically centered + > div { + display: flex; + align-items: center; + } + + @media (max-width: 1279px) { // xl breakpoint - hide Machines and Refillers + grid-template-columns: 140px 2fr 2fr 1.5fr 1fr 80px; + } + + @media (max-width: 1023px) { // lg breakpoint - hide Status and Contact + grid-template-columns: 140px 2fr 2fr 80px; + } + + @media (max-width: 767px) { // md breakpoint - hide User ID + grid-template-columns: 2fr 2fr 80px; + } + + @media (max-width: 639px) { // sm breakpoint - hide Email + grid-template-columns: 1fr 80px; + } +} + +/* Machine and Refiller columns - already centered by parent grid item */ +.client-grid > div:nth-child(6), +.client-grid > div:nth-child(7) { + /* Machine ID in monospace */ + .font-mono { + font-family: 'Courier New', monospace; + } + + /* Refiller names in medium weight */ + .font-medium { + font-weight: 500; + } + + /* For multiple items, use flex-col but still centered */ + > div { + display: flex; + flex-direction: column; + gap: 4px; + } +} + +/* Material form field customizations */ +.fuse-mat-dense { + .mat-mdc-form-field-subscript-wrapper { + display: none; + } + + .mat-mdc-form-field-infix { + min-height: 40px; + } +} + +.fuse-mat-rounded { + .mat-mdc-form-field-flex { + border-radius: 20px; + background-color: rgba(0, 0, 0, 0.04); + border: none; + } + + &.mat-focused .mat-mdc-form-field-flex { + background-color: rgba(0, 0, 0, 0.06); + } +} + +/* Icon size utilities */ +.icon-size-5 { + width: 20px; + height: 20px; + font-size: 20px; +} + +.icon-size-4 { + width: 16px; + height: 16px; + font-size: 16px; +} + +.icon-size-3 { + width: 14px; + height: 14px; + font-size: 14px; +} + +/* Avatar styles */ +.client-avatar { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + font-weight: 600; + font-size: 14px; + text-transform: uppercase; +} + +/* Status badge styles */ +.status-badge { + display: inline-flex; + align-items: center; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: 500; + + &.active, + &.online { + background-color: #dcfce7; + color: #166534; + } + + &.inactive, + &.offline { + background-color: #f3f4f6; + color: #374151; + } + + &.maintenance { + background-color: #fef3c7; + color: #92400e; + } +} + +/* Machine card styles */ +.machine-card { + background: #ffffff; + border-radius: 8px; + padding: 16px; + border: 1px solid #e5e7eb; + transition: all 0.2s ease; + + &:hover { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + border-color: #3b82f6; + } +} + +/* Refiller card styles */ +.refiller-card { + background: #f0fdf4; + border-radius: 8px; + padding: 16px; + border: 1px solid #bbf7d0; + transition: all 0.2s ease; + + &:hover { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + border-color: #22c55e; + } +} + +/* Empty state styles */ +.empty-state { + text-align: center; + padding: 32px; + background-color: #f9fafb; + border-radius: 8px; + border: 2px dashed #d1d5db; + + mat-icon { + font-size: 48px; + width: 48px; + height: 48px; + color: #9ca3af; + margin-bottom: 8px; + } + + p { + color: #6b7280; + font-size: 14px; + margin: 8px 0 0 0; + } +} + +/* Details panel */ +.details-panel { + background: #ffffff; + border-radius: 8px; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); +} + +/* Section headers in details */ +.section-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; + padding-bottom: 8px; + border-bottom: 2px solid #e5e7eb; + + h4 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #1f2937; + } + + mat-icon { + font-size: 20px; + width: 20px; + height: 20px; + } +} + +/* Dark mode support */ +@media (prefers-color-scheme: dark) { + .machine-card { + background: #1f2937; + border-color: #374151; + color: #f9fafb; + + &:hover { + border-color: #60a5fa; + } + } + + .refiller-card { + background: #064e3b; + border-color: #059669; + color: #f0fdf4; + + &:hover { + border-color: #10b981; + } + } + + .empty-state { + background-color: #1f2937; + border-color: #4b5563; + + p { + color: #9ca3af; + } + } + + .details-panel { + background: #1f2937; + color: #f9fafb; + } + + .section-header { + border-bottom-color: #374151; + + h4 { + color: #f9fafb; + } + } + + .fuse-mat-rounded .mat-mdc-form-field-flex { + background-color: rgba(255, 255, 255, 0.06); + } + + .fuse-mat-rounded.mat-focused .mat-mdc-form-field-flex { + background-color: rgba(255, 255, 255, 0.08); + } +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .client-grid { + gap: 8px; + padding: 12px 16px; + } + + .machine-card, + .refiller-card { + padding: 12px; + } +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* Animations */ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.machine-card, +.refiller-card { + animation: slideIn 0.3s ease-out; +} + +/* Badge animations */ +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.status-badge span { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* Hover effects */ +.client-grid > div { + transition: background-color 0.2s ease; +} + +.client-grid > div:hover { + background-color: rgba(0, 0, 0, 0.02); +} + +/* Focus states for accessibility */ +.mat-mdc-button:focus, +.mat-mdc-raised-button:focus { + outline: 2px solid #3b82f6; + outline-offset: 2px; +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .status-badge { + border: 2px solid currentColor; + } + + .machine-card, + .refiller-card { + border-width: 2px; + } +} + +/* Print styles */ +@media print { + .mat-mdc-button, + .mat-paginator, + .search-input { + display: none; + } + + .details-panel { + box-shadow: none; + border: 1px solid #000; + } +} + +/* Loading states */ +.loading-overlay { + position: relative; + + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.8); + z-index: 10; + pointer-events: none; + opacity: 0; + transition: opacity 0.2s ease-in-out; + } + + &.loading::after { + opacity: 1; + pointer-events: all; + } +} + +/* Count badge special styles */ +.count-badge { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; + + mat-icon { + font-size: 12px; + width: 12px; + height: 12px; + } +} + +/* Grid responsiveness helper */ +@media (max-width: 1279px) { + .grid-cols-3 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 767px) { + .grid-cols-2, + .grid-cols-3 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.ts index fc3d428..955cc90 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.ts +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.component.ts @@ -1,12 +1,294 @@ -import { Component } from '@angular/core'; +// client-list.component.ts +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, AfterViewInit } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { MatPaginator, MatPaginatorModule, PageEvent } from '@angular/material/paginator'; +import { MatSort, MatSortModule, Sort } from '@angular/material/sort'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { CommonModule } from '@angular/common'; +import { Observable, Subject, BehaviorSubject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, takeUntil, startWith } from 'rxjs/operators'; +import { ClientListService, ClientDetail, ClientMachine, ClientRefiller } from './client-list.service'; @Component({ selector: 'app-client-list', standalone: true, - imports: [], + imports: [ + CommonModule, + ReactiveFormsModule, + MatTableModule, + MatPaginatorModule, + MatSortModule, + MatProgressBarModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatExpansionModule + ], templateUrl: './client-list.component.html', styleUrl: './client-list.component.scss' }) -export class ClientListComponent { +export class ClientListComponent implements OnInit, OnDestroy, AfterViewInit { + @ViewChild(MatPaginator) paginator!: MatPaginator; + @ViewChild(MatSort) sort!: MatSort; -} + // Observables + clients$!: Observable; + private _unsubscribeAll: Subject = new Subject(); + + // Data + dataSource = new MatTableDataSource(); + private _clients: BehaviorSubject = new BehaviorSubject([]); + + // UI State + isLoading: boolean = false; + selectedClient: ClientDetail | null = null; + + // Search + searchInputControl: FormControl = new FormControl(''); + + // Pagination + pagination = { + length: 0, + page: 0, + size: 10 + }; + + constructor( + private _clientListService: ClientListService, + private _changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.clients$ = this._clients.asObservable(); + + // Setup search + this.searchInputControl.valueChanges + .pipe( + takeUntil(this._unsubscribeAll), + debounceTime(300), + distinctUntilChanged(), + startWith('') + ) + .subscribe(() => { + this.filterClients(); + }); + + // Load clients + this.loadClients(); + } + + ngAfterViewInit(): void { + if (this.paginator) { + this.paginator.page + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((page: PageEvent) => { + this.pagination.page = page.pageIndex; + this.pagination.size = page.pageSize; + this.filterClients(); + }); + } + + if (this.sort) { + this.sort.sortChange + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((sort: Sort) => { + this.sortClients(sort); + }); + } + } + + ngOnDestroy(): void { + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); + } + + /** + * Load all clients with their details + */ + loadClients(): void { + this.isLoading = true; + + this._clientListService.getClientsWithDetails() + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe( + (clients: ClientDetail[]) => { + console.log('Loaded clients:', clients); + this._clients.next(clients); + this.pagination.length = clients.length; + this.isLoading = false; + this.filterClients(); + this._changeDetectorRef.markForCheck(); + }, + (error) => { + console.error('Error loading clients:', error); + this.isLoading = false; + } + ); + } + + /** + * Filter clients based on search + */ + filterClients(): void { + let filteredClients = this._clients.getValue(); + const searchTerm = this.searchInputControl.value?.toLowerCase() || ''; + + if (searchTerm) { + filteredClients = filteredClients.filter(client => + client.username.toLowerCase().includes(searchTerm) || + client.email.toLowerCase().includes(searchTerm) || + client.contact.includes(searchTerm) || + (client.user_id && client.user_id.toLowerCase().includes(searchTerm)) || + client.refillers.some(r => + r.username.toLowerCase().includes(searchTerm) || + r.email.toLowerCase().includes(searchTerm) + ) + ); + } + + this.pagination.length = filteredClients.length; + + const start = this.pagination.page * this.pagination.size; + const end = start + this.pagination.size; + filteredClients = filteredClients.slice(start, end); + + this.dataSource.data = filteredClients; + this._changeDetectorRef.markForCheck(); + } + + /** + * Sort clients + */ + sortClients(sort: Sort): void { + const clients = this._clients.getValue().slice(); + + if (!sort.active || sort.direction === '') { + this._clients.next(clients); + return; + } + + const sortedClients = clients.sort((a, b) => { + const isAsc = sort.direction === 'asc'; + switch (sort.active) { + case 'user_id': return this.compare(a.user_id, b.user_id, isAsc); + case 'username': return this.compare(a.username, b.username, isAsc); + case 'email': return this.compare(a.email, b.email, isAsc); + case 'contact': return this.compare(a.contact, b.contact, isAsc); + case 'machines_count': return this.compare(a.machines_count, b.machines_count, isAsc); + case 'refillers_count': return this.compare(a.refillers_count, b.refillers_count, isAsc); + default: return 0; + } + }); + + this._clients.next(sortedClients); + this.filterClients(); + } + + private compare(a: string | number | undefined, b: string | number | undefined, isAsc: boolean): number { + return (a! < b! ? -1 : 1) * (isAsc ? 1 : -1); + } + + /** + * Toggle client details + */ + toggleDetails(clientId?: number): void { + if (this.selectedClient && this.selectedClient.id === clientId) { + this.closeDetails(); + return; + } + + const client = this._clients.getValue().find(c => c.id === clientId); + if (client) { + this.selectedClient = { ...client }; + + // Load fresh data for machines and refillers + this.loadClientDetails(client.id); + + this._changeDetectorRef.markForCheck(); + } + } + + /** + * Load detailed information for a client + */ + private loadClientDetails(clientId: number): void { + // Load all machines and filter by client_id + this._clientListService.getClientsWithDetails() + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe( + (clients) => { + const clientData = clients.find(c => c.id === clientId); + if (clientData && this.selectedClient && this.selectedClient.id === clientId) { + // Update machines + this.selectedClient.machines = clientData.machines; + this.selectedClient.machines_count = clientData.machines_count; + + // Update refillers + this.selectedClient.refillers = clientData.refillers; + this.selectedClient.refillers_count = clientData.refillers_count; + + this._changeDetectorRef.markForCheck(); + } + }, + (error) => { + console.error('Error loading client details:', error); + } + ); + } + + /** + * Close details panel + */ + closeDetails(): void { + this.selectedClient = null; + this._changeDetectorRef.markForCheck(); + } + + /** + * Get full file URL + */ + getFullFileUrl(path: string | undefined): string | null { + return this._clientListService.getFullFileUrl(path); + } + + /** + * Track by function for ngFor + */ + trackByFn(index: number, item: any): any { + return item.id || index; + } + + /** + * Get status badge class + */ + getStatusClass(status: string): string { + switch (status.toLowerCase()) { + case 'active': + case 'online': + return 'bg-green-100 text-green-800'; + case 'inactive': + case 'offline': + return 'bg-gray-100 text-gray-800'; + case 'maintenance': + return 'bg-yellow-100 text-yellow-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + } + + /** + * Refresh data + */ + refresh(): void { + this.loadClients(); + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.service.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.service.ts new file mode 100644 index 0000000..7ab3c5c --- /dev/null +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/client-list/client-list.service.ts @@ -0,0 +1,182 @@ +// client-list.service.ts +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { environment } from '@environments/environment'; + +export interface ClientMachine { + machine_id: string; + machine_model: string; + machine_type: string; + branch_name: string; + operation_status: string; + connection_status: string; +} + +export interface ClientRefiller { + id: number; + user_id: string; + username: string; + email: string; + contact: string; + assigned_machines_count: number; + assigned_machines: Array<{ + machine_id: string; + machine_model: string; + branch_name: string; + }>; +} + +export interface ClientDetail { + id: number; + user_id: string; + username: string; + email: string; + contact: string; + photo?: string; + company_logo?: string; + created_at: string; + user_status: string; + + // Machines owned by this client + machines: ClientMachine[]; + machines_count: number; + + // Refillers assigned to this client + refillers: ClientRefiller[]; + refillers_count: number; +} + +@Injectable({ + providedIn: 'root' +}) +export class ClientListService { + private apiUrl = environment.apiUrl; + private backendBaseUrl = environment.apiUrl; + + constructor(private http: HttpClient) {} + + /** + * Get all clients with their machines and refillers + */ + getClientsWithDetails(): Observable { + // First get all users + return this.http.get(`${this.apiUrl}/users`).pipe( + map(users => { + // Filter only clients + const clients = users.filter(u => u.roles === 'Client'); + + // Also get all machines to map to clients + return { clients, users }; + }), + // Now fetch machines + switchMap(({ clients, users }) => { + return this.http.get(`${this.apiUrl}/machines`).pipe( + map(machines => { + // Transform clients with their machines and refillers + return clients.map(client => + this.transformToClientDetail(client, users, machines) + ); + }) + ); + }) + ); + } + + /** + * Get single client with details + */ + getClientById(clientId: number): Observable { + // Fetch client, users, and machines in parallel + return this.http.get(`${this.apiUrl}/users/${clientId}`).pipe( + switchMap(client => { + // Get all users and machines + return this.http.get(`${this.apiUrl}/users`).pipe( + switchMap(users => { + return this.http.get(`${this.apiUrl}/machines`).pipe( + map(machines => this.transformToClientDetail(client, users, machines)) + ); + }) + ); + }) + ); + } + + /** + * Get machines for a specific client + */ + getClientMachines(clientId: number): Observable { + return this.http.get(`${this.apiUrl}/clients/${clientId}/machines`); + } + + /** + * Get refillers assigned to a specific client + */ + getClientRefillers(clientId: number): Observable { + return this.http.get(`${this.apiUrl}/users/${clientId}/assigned-refillers`); + } + + /** + * Transform user data to ClientDetail format + */ + private transformToClientDetail(client: any, allUsers: any[], allMachines: any[]): ClientDetail { + // Get machines owned by this client (client_id matches) + const machines: ClientMachine[] = allMachines + .filter(machine => machine.client_id === client.id) + .map(machine => ({ + machine_id: machine.machine_id, + machine_model: machine.machine_model, + machine_type: machine.machine_type, + branch_name: machine.branch_name, + operation_status: machine.operation_status, + connection_status: machine.connection_status + })); + + // Get refillers assigned to this client + const refillers = allUsers + .filter(u => u.roles === 'Refiller' && u.assigned_to === client.id) + .map(refiller => ({ + id: refiller.id, + user_id: refiller.user_id, + username: refiller.username, + email: refiller.email, + contact: refiller.contact, + assigned_machines_count: refiller.assigned_machines_count || 0, + assigned_machines: refiller.assigned_machines || [] + })); + + return { + id: client.id, + user_id: client.user_id, + username: client.username, + email: client.email, + contact: client.contact, + photo: client.photo, + company_logo: client.company_logo, + created_at: client.created_at, + user_status: client.user_status, + machines: machines, + machines_count: machines.length, + refillers: refillers, + refillers_count: refillers.length + }; + } + + /** + * Get full file URL (same as UserService) + */ + getFullFileUrl(path: string | undefined | null): string | null { + if (!path) return null; + + if (path.startsWith('http://') || path.startsWith('https://')) { + return path; + } + + if (path.startsWith('/')) { + return this.backendBaseUrl + path; + } + + return this.backendBaseUrl + '/' + path; + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.html b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.html index f977d37..48f7036 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.html +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.html @@ -1 +1,268 @@ -

company-admin-list works!

+ +
+ +
+ + @if (isLoading) { +
+ +
+ } + + +
Company Admin Management
+ + +
+ + + + + + + + +
+
+ + +
+ +
+ @if (admins$ | async; as admins) { + @if (filteredAdmins.length > 0 || searchInputControl.value) { +
+ +
+ +
Admin Name
+ + + + + +
+ + + @for (admin of filteredAdmins; track trackByFn($index, admin)) { +
+ + + + +
+
+ @if (admin.photo) { + + } @else { + + {{ admin.username.charAt(0) }} + + } +
+
+
{{ admin.username }}
+ @if (admin.created_by_username) { +
+ person_add + by {{ admin.created_by_username }} +
+ } +
+
+ + + + + + + + + + + + + + + +
+ + + @if (selectedAdmin?.id === admin.id) { +
+
+ +
+
+
+ @if (selectedAdmin.photo) { + + } @else { + + {{ selectedAdmin.username.charAt(0) }} + + } +
+
+

{{ selectedAdmin.username }}

+

{{ selectedAdmin.user_id }}

+
+ + {{ selectedAdmin.roles }} + + @if (selectedAdmin.user_status === 'Active') { + + + Active + + } +
+
+
+ @if (selectedAdmin.company_logo) { +
+ Company logo +
+ } +
+ + +
+ +
+

+ contact_mail + Contact Information +

+
+
+ email + {{ selectedAdmin.email }} +
+
+ phone + {{ selectedAdmin.contact }} +
+
+
+ + +
+

+ person_add + Created By +

+
+
+ account_circle + {{ selectedAdmin.created_by_username || 'System' }} +
+
+ calendar_today + {{ selectedAdmin.created_at | date:'short' }} +
+
+
+ + +
+

+ info + Account Status +

+
+
+ verified_user + {{ selectedAdmin.user_status }} +
+ @if (selectedAdmin.updated_at) { +
+ update + Updated: {{ selectedAdmin.updated_at | date:'short' }} +
+ } +
+
+
+ + +
+ +
+
+
+ } + } +
+ + + + + } @else { +
+ No admin users found! +
+ } + } +
+
+
\ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.scss b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.scss index e69de29..0d99bbd 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.scss +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.scss @@ -0,0 +1,237 @@ +/* company-admin-list.component.scss */ + +/* Admin grid layout - 7 COLUMNS: User ID, Name, Email, Contact, Role, Status, Details */ +.admin-grid { + // Default: All 7 columns visible on XL screens + grid-template-columns: 140px 2fr 2fr 1.5fr 1.2fr 1fr 80px; + + // Ensure all items are vertically centered + > div { + display: flex; + align-items: center; + } + + @media (max-width: 1023px) { // lg breakpoint - hide Role and Status + grid-template-columns: 140px 2fr 2fr 1.5fr 80px; + } + + @media (max-width: 767px) { // md breakpoint - hide User ID + grid-template-columns: 2fr 2fr 1.5fr 80px; + } + + @media (max-width: 639px) { // sm breakpoint - hide Email + grid-template-columns: 1fr 80px; + } +} + +/* Hover effect for rows */ +.admin-grid:not(.sticky) { + transition: background-color 0.2s ease; + + &:hover { + background-color: rgba(0, 0, 0, 0.02); + } +} + +/* Dark mode hover */ +@media (prefers-color-scheme: dark) { + .admin-grid:not(.sticky):hover { + background-color: rgba(255, 255, 255, 0.05); + } +} + +/* Avatar styling */ +.admin-grid img { + transition: transform 0.2s ease; +} + +.admin-grid:hover img { + transform: scale(1.05); +} + +/* Status badge animation */ +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* Details panel animation */ +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.admin-grid + .grid { + animation: slideDown 0.3s ease-out; +} + +/* Role badge colors */ +.bg-orange-100 { + background-color: #ffedd5; +} + +.text-orange-800 { + color: #9a3412; +} + +.bg-amber-100 { + background-color: #fef3c7; +} + +.text-amber-800 { + color: #92400e; +} + +.bg-red-100 { + background-color: #fee2e2; +} + +.text-red-800 { + color: #991b1b; +} + +/* Status badge colors */ +.bg-green-100 { + background-color: #dcfce7; +} + +.text-green-800 { + color: #166534; +} + +.bg-green-400 { + background-color: #4ade80; +} + +.bg-gray-100 { + background-color: #f3f4f6; +} + +.text-gray-800 { + color: #1f2937; +} + +.bg-gray-400 { + background-color: #9ca3af; +} + +/* Details panel card styling */ +.bg-white { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); +} + +/* Responsive text sizing */ +@media (max-width: 639px) { + .text-4xl { + font-size: 1.875rem; + line-height: 2.25rem; + } +} + +/* Loading state */ +.pointer-events-none { + pointer-events: none; + opacity: 0.6; +} + +/* Smooth transitions */ +button { + transition: all 0.2s ease; +} + +button:hover { + transform: translateY(-1px); +} + +button:active { + transform: translateY(0); +} + +/* Icon sizing */ +.icon-size-3 { + height: 0.75rem; + width: 0.75rem; + font-size: 0.75rem; +} + +.icon-size-4 { + height: 1rem; + width: 1rem; + font-size: 1rem; +} + +.icon-size-5 { + height: 1.25rem; + width: 1.25rem; + font-size: 1.25rem; +} + +/* Scrollbar styling */ +.sm\:overflow-y-auto::-webkit-scrollbar { + width: 8px; +} + +.sm\:overflow-y-auto::-webkit-scrollbar-track { + background: transparent; +} + +.sm\:overflow-y-auto::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; +} + +.sm\:overflow-y-auto::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.3); +} + +/* Dark mode scrollbar */ +@media (prefers-color-scheme: dark) { + .sm\:overflow-y-auto::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.2); + } + + .sm\:overflow-y-auto::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.3); + } +} + +/* Print styles */ +@media print { + .admin-grid { + grid-template-columns: 100px 1fr 1fr 120px 100px 80px; + page-break-inside: avoid; + } + + button, mat-paginator { + display: none !important; + } +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .admin-grid:hover { + outline: 2px solid currentColor; + } + + .bg-green-100, + .bg-gray-100, + .bg-orange-100, + .bg-amber-100, + .bg-red-100 { + border: 1px solid currentColor; + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.ts index 735a6e0..eec330a 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.ts +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.component.ts @@ -1,12 +1,246 @@ -import { Component } from '@angular/core'; +// company-admin-list.component.ts +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule, FormsModule, FormControl } from '@angular/forms'; +import { MatTableModule } from '@angular/material/table'; +import { MatPaginatorModule, MatPaginator, PageEvent } from '@angular/material/paginator'; +import { MatSortModule, MatSort, Sort } from '@angular/material/sort'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { Observable, Subject, BehaviorSubject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, map, startWith, takeUntil } from 'rxjs/operators'; +import { CompanyAdminListService, AdminDetail } from './company-admin-list.service'; @Component({ selector: 'app-company-admin-list', standalone: true, - imports: [], + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatTableModule, + MatPaginatorModule, + MatSortModule, + MatProgressBarModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule + ], templateUrl: './company-admin-list.component.html', styleUrl: './company-admin-list.component.scss' }) -export class CompanyAdminListComponent { +export class CompanyAdminListComponent implements OnInit, OnDestroy { + @ViewChild(MatPaginator) paginator!: MatPaginator; + @ViewChild(MatSort) sort!: MatSort; -} + // Observables + admins$!: Observable; + private _unsubscribeAll: Subject = new Subject(); + + // Data + private _admins: BehaviorSubject = new BehaviorSubject([]); + filteredAdmins: AdminDetail[] = []; + + // UI State + isLoading: boolean = false; + selectedAdmin: AdminDetail | null = null; + + // Search + searchInputControl: FormControl = new FormControl(''); + + // Pagination + pagination = { + length: 0, + page: 0, + size: 10, + pageSizeOptions: [5, 10, 25, 100] + }; + + constructor( + private _adminService: CompanyAdminListService, + private _changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.admins$ = this._admins.asObservable(); + + // Search functionality + this.searchInputControl.valueChanges + .pipe( + takeUntil(this._unsubscribeAll), + debounceTime(300), + distinctUntilChanged(), + startWith('') + ) + .subscribe(() => { + this.filterAdmins(); + }); + + // Load admin users + this.loadAdmins(); + } + + ngOnDestroy(): void { + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); + } + + /** + * Load all admin users (Management, SuperAdmin, Admin) + */ + loadAdmins(): void { + this.isLoading = true; + + this._adminService.getAdminsWithDetails() + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe({ + next: (admins) => { + this._admins.next(admins); + this.pagination.length = admins.length; + this.isLoading = false; + this.filterAdmins(); + this._changeDetectorRef.markForCheck(); + + console.log('Loaded admin users:', admins); + }, + error: (error) => { + console.error('Error loading admin users:', error); + this.isLoading = false; + this._changeDetectorRef.markForCheck(); + } + }); + } + + /** + * Filter admins based on search term + */ + filterAdmins(): void { + let admins = this._admins.getValue(); + const searchTerm = this.searchInputControl.value?.toLowerCase() || ''; + + // Apply search filter + if (searchTerm) { + admins = admins.filter(admin => + admin.username.toLowerCase().includes(searchTerm) || + admin.email.toLowerCase().includes(searchTerm) || + admin.contact.includes(searchTerm) || + admin.roles.toLowerCase().includes(searchTerm) || + (admin.user_id && admin.user_id.toLowerCase().includes(searchTerm)) || + (admin.created_by_username && admin.created_by_username.toLowerCase().includes(searchTerm)) + ); + } + + // Update pagination + this.pagination.length = admins.length; + + // Apply pagination + const start = this.pagination.page * this.pagination.size; + const end = start + this.pagination.size; + this.filteredAdmins = admins.slice(start, end); + + this._changeDetectorRef.markForCheck(); + } + + /** + * Sort admins + */ + sortAdmins(sort: Sort): void { + const admins = this._admins.getValue().slice(); + + if (!sort.active || sort.direction === '') { + this._admins.next(admins); + this.filterAdmins(); + return; + } + + const sortedAdmins = admins.sort((a, b) => { + const isAsc = sort.direction === 'asc'; + switch (sort.active) { + case 'user_id': return this.compare(a.user_id, b.user_id, isAsc); + case 'username': return this.compare(a.username, b.username, isAsc); + case 'email': return this.compare(a.email, b.email, isAsc); + case 'contact': return this.compare(a.contact, b.contact, isAsc); + case 'roles': return this.compare(a.roles, b.roles, isAsc); + case 'user_status': return this.compare(a.user_status, b.user_status, isAsc); + default: return 0; + } + }); + + this._admins.next(sortedAdmins); + this.filterAdmins(); + } + + /** + * Compare function for sorting + */ + private compare(a: string | number | undefined, b: string | number | undefined, isAsc: boolean): number { + if (a === undefined || a === null) a = ''; + if (b === undefined || b === null) b = ''; + return (a < b ? -1 : 1) * (isAsc ? 1 : -1); + } + + /** + * Handle pagination change + */ + onPageChange(event: PageEvent): void { + this.pagination.page = event.pageIndex; + this.pagination.size = event.pageSize; + this.filterAdmins(); + } + + /** + * Handle sort change + */ + onSortChange(sort: Sort): void { + this.sortAdmins(sort); + } + + /** + * Toggle admin details + */ + toggleDetails(adminId: number): void { + if (this.selectedAdmin && this.selectedAdmin.id === adminId) { + this.closeDetails(); + return; + } + + const admin = this._admins.getValue().find(a => a.id === adminId); + if (admin) { + this.selectedAdmin = { ...admin }; + this._changeDetectorRef.markForCheck(); + } + } + + /** + * Close details panel + */ + closeDetails(): void { + this.selectedAdmin = null; + this._changeDetectorRef.markForCheck(); + } + + /** + * Refresh admin list + */ + refreshAdmins(): void { + this.loadAdmins(); + } + + /** + * Get full file URL + */ + getFullFileUrl(path: string | undefined): string | null { + return this._adminService.getFullFileUrl(path); + } + + /** + * Track by function for ngFor + */ + trackByFn(index: number, item: AdminDetail): any { + return item.id || index; + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.service.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.service.ts new file mode 100644 index 0000000..d477d55 --- /dev/null +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/company-admin-list/company-admin-list.service.ts @@ -0,0 +1,124 @@ +// company-admin-list.service.ts +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { environment } from 'environments/environment'; + +export interface AdminDetail { + id: number; + user_id: string; + username: string; + email: string; + contact: string; + roles: string; + photo?: string; + company_logo?: string; + created_at: string; + updated_at?: string; + user_status: string; + created_by?: number; + created_by_username?: string; + assigned_to?: number; + assigned_to_username?: string; +} + +@Injectable({ + providedIn: 'root' +}) +export class CompanyAdminListService { + private apiUrl = environment.apiUrl; + + constructor(private http: HttpClient) {} + + /** + * Get all admin users (Management, SuperAdmin, Admin) + */ + getAdminsWithDetails(): Observable { + return this.http.get(`${this.apiUrl}/users`).pipe( + map(users => { + // Filter only admin roles: Management, SuperAdmin, Admin + const adminUsers = users.filter(u => + u.roles === 'Management' || + u.roles === 'SuperAdmin' || + u.roles === 'Admin' + ); + + // Transform to AdminDetail format + return adminUsers.map(admin => this.transformToAdminDetail(admin, users)); + }) + ); + } + + /** + * Get single admin by ID + */ + getAdminById(adminId: number): Observable { + return this.http.get(`${this.apiUrl}/users/${adminId}`).pipe( + switchMap(admin => { + return this.http.get(`${this.apiUrl}/users`).pipe( + map(users => this.transformToAdminDetail(admin, users)) + ); + }) + ); + } + + /** + * Transform user data to AdminDetail format + */ + private transformToAdminDetail(admin: any, allUsers: any[]): AdminDetail { + // Find who created this admin + let createdByUsername = 'System'; + if (admin.created_by) { + const creator = allUsers.find(u => u.id === admin.created_by); + if (creator) { + createdByUsername = creator.username; + } + } + + // Find who this admin is assigned to (if applicable) + let assignedToUsername = null; + if (admin.assigned_to) { + const assignedUser = allUsers.find(u => u.id === admin.assigned_to); + if (assignedUser) { + assignedToUsername = assignedUser.username; + } + } + + return { + id: admin.id, + user_id: admin.user_id, + username: admin.username, + email: admin.email, + contact: admin.contact, + roles: admin.roles, + photo: admin.photo, + company_logo: admin.company_logo, + created_at: admin.created_at, + updated_at: admin.updated_at, + user_status: admin.user_status, + created_by: admin.created_by, + created_by_username: createdByUsername, + assigned_to: admin.assigned_to, + assigned_to_username: assignedToUsername + }; + } + + /** + * Convert relative file path to full URL + */ + getFullFileUrl(path: string | undefined): string | null { + if (!path) return null; + + // If already a full URL, return as is + if (path.startsWith('http://') || path.startsWith('https://')) { + return path; + } + + // Remove leading slash if present + const cleanPath = path.startsWith('/') ? path.substring(1) : path; + + // Construct full URL + return `${this.apiUrl}/${cleanPath}`; + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/machines/machine-details/machine-details.component.html b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/machines/machine-details/machine-details.component.html index 2a30f6a..049b5bf 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/machines/machine-details/machine-details.component.html +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/machines/machine-details/machine-details.component.html @@ -1,12 +1,15 @@ +
+
-
@@ -17,8 +20,13 @@
+ +
+ Loading... +
+ -
+

Information

@@ -61,7 +69,7 @@
-
+
{{row.name}} @@ -73,19 +81,28 @@
-
+
+ +
{{slot.name}}
- + {{slot.enabled ? 'Enabled' : 'Disabled'}}
+ +
@@ -95,16 +112,27 @@

Assign Product

+
- + + + + +
-

{{slot.product.product_name}}

-
@@ -122,7 +150,7 @@
- + } +

Documents & Files

diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.scss b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.scss index 00d5a70..dc98e46 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.scss +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.scss @@ -1,9 +1,13 @@ -/* User grid layout */ +/* User grid layout - NOW 10 COLUMNS: ID, Username, Email, Contact, Role, Status, Created By, Assigned To, Machines, Details */ .user-grid { - // Default: ID, Username, Email, Contact, Role, Status, Details (7 columns) - grid-template-columns: 60px 2fr 2fr 1.5fr 1fr 1fr 80px; + // Default: All 10 columns visible on XL screens + grid-template-columns: 60px 2fr 2fr 1.5fr 1fr 1fr 150px 150px 120px 80px; - @media (max-width: 1023px) { // lg breakpoint - hide contact and role + @media (max-width: 1279px) { // xl breakpoint - hide Assigned To and Machines + grid-template-columns: 60px 2fr 2fr 1.5fr 1fr 1fr 150px 80px; + } + + @media (max-width: 1023px) { // lg breakpoint - hide Created By, contact and role grid-template-columns: 60px 2fr 2fr 1fr 80px; } @@ -16,6 +20,69 @@ } } +/* Created By Column Styling */ +.user-grid > div:nth-child(7) { + display: flex; + align-items: center; + gap: 6px; +} + +.user-grid > div:nth-child(7) mat-icon { + color: #6b7280; + font-size: 16px; + width: 16px; + height: 16px; +} + +.user-grid > div:nth-child(7) span { + font-size: 13px; + color: #374151; +} + +/* ⭐ NEW: Assigned To Column Styling */ +.user-grid > div:nth-child(8) { + display: flex; + align-items: center; + gap: 6px; +} + +.user-grid > div:nth-child(8) mat-icon { + font-size: 16px; + width: 16px; + height: 16px; +} + +.user-grid > div:nth-child(8) span { + font-size: 13px; +} + +/* ⭐ NEW: Machines Column Styling */ +.user-grid > div:nth-child(9) { + display: flex; + align-items: center; + gap: 4px; +} + +.user-grid > div:nth-child(9) mat-icon { + font-size: 14px; + width: 14px; + height: 14px; +} + +.user-grid > div:nth-child(9) span { + font-size: 12px; +} + +.dark .user-grid > div:nth-child(7) mat-icon, +.dark .user-grid > div:nth-child(8) mat-icon { + color: #9ca3af; +} + +.dark .user-grid > div:nth-child(7) span, +.dark .user-grid > div:nth-child(8) span { + color: #d1d5db; +} + /* Material form field customizations */ .fuse-mat-dense { .mat-mdc-form-field-subscript-wrapper { @@ -52,6 +119,12 @@ font-size: 16px; } +.icon-size-3 { + width: 14px; + height: 14px; + font-size: 14px; +} + /* Custom button styles */ .mat-mdc-button { &.mat-primary { @@ -271,6 +344,7 @@ border: 1px solid currentColor; } } + /* Additional styles for user.component.scss */ /* File upload section styles */ @@ -585,6 +659,7 @@ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } } + .doc-type-badge { &.aadhar { background: #fef2f2; color: #991b1b; } &.pan { background: #eff6ff; color: #1e40af; } diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.ts index ced9684..1d793ca 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.ts +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.component.ts @@ -1,5 +1,5 @@ -// user.component.ts - UPDATED WITH DYNAMIC ROLE FETCHING -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; +// user.component.ts - WITH REAL-TIME ROLE UPDATES +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, AfterViewInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatPaginator, MatPaginatorModule, PageEvent } from '@angular/material/paginator'; @@ -17,7 +17,22 @@ import { Observable, Subject, BehaviorSubject, combineLatest } from 'rxjs'; import { debounceTime, distinctUntilChanged, map, startWith, takeUntil } from 'rxjs/operators'; import { User, UserService, UserDocument, DocumentWithMetadata } from '../user/user.service'; import { AuthService } from 'app/core/auth/auth.service'; -import { RoleService, Role } from '../role-management/role-management.service'; // IMPORT ROLE SERVICE +import { RoleService, Role } from '../role-management/role-management.service'; +import { RoleStateService } from 'app/core/services/role-state.service'; + +// ⭐ NEW: Import types for assignment +interface Client { + id: number; + username: string; + email: string; +} + +interface Machine { + machine_id: string; + machine_model: string; + branch_name: string; + operation_status: string; +} @Component({ selector: 'app-user', @@ -41,7 +56,7 @@ import { RoleService, Role } from '../role-management/role-management.service'; MatTooltipModule ] }) -export class UserComponent implements OnInit, OnDestroy { +export class UserComponent implements OnInit, OnDestroy, AfterViewInit { @ViewChild(MatPaginator) paginator!: MatPaginator; @ViewChild(MatSort) sort!: MatSort; @@ -58,13 +73,25 @@ export class UserComponent implements OnInit, OnDestroy { selectedUser: User | null = null; flashMessage: 'success' | 'error' | null = null; - // Role-based properties - UPDATED TO USE DYNAMIC ROLES + // Role-based properties currentUserRole: string | null = null; isClient: boolean = false; isAdmin: boolean = false; availableRoles: { value: string; label: string }[] = []; - allRoles: Role[] = []; // Store all roles from database + allRoles: Role[] = []; rolesLoading: boolean = false; + rolesLoaded: boolean = false; + + // ⭐ NEW: Option 3 - Client Assignment + availableClients: Client[] = []; + clientsLoading: boolean = false; + showClientAssignment: boolean = false; + + // ⭐ NEW: Option 3 - Machine Assignment + availableMachines: Machine[] = []; + selectedMachineIds: string[] = []; + showMachineAssignment: boolean = false; + machinesLoading: boolean = false; // Forms searchInputControl: FormControl = new FormControl(''); @@ -106,16 +133,18 @@ export class UserComponent implements OnInit, OnDestroy { private _formBuilder: FormBuilder, private _changeDetectorRef: ChangeDetectorRef, private _authService: AuthService, - private _roleService: RoleService // INJECT ROLE SERVICE + private _roleService: RoleService, + private _roleStateService: RoleStateService // ⭐ NEW: Inject RoleStateService ) { - // Initialize form + // Initialize form with assigned_to this.selectedUserForm = this._formBuilder.group({ username: ['', [Validators.required]], email: ['', [Validators.required, Validators.email]], contact: ['', [Validators.required, Validators.pattern('^[0-9]{10}$')]], roles: ['', [Validators.required]], user_status: ['Active'], - password: ['', [Validators.required, Validators.minLength(6)]] + password: [''], + assigned_to: [''] }); } @@ -126,10 +155,40 @@ export class UserComponent implements OnInit, OnDestroy { this.isAdmin = this._authService.hasAnyRole(['Management', 'SuperAdmin', 'Admin']); console.log('User Management - Current Role:', this.currentUserRole); + console.log('Is Client:', this.isClient); + console.log('Is Admin:', this.isAdmin); - // Load roles from database + // Load roles from database FIRST this.loadRolesFromDatabase(); + // ⭐ NEW: Subscribe to role changes from Role Management + this.subscribeToRoleChanges(); + + // Load clients for dropdown (only for admins) + if (this.isAdmin) { + this.loadClients(); + } + + // Watch for role and client changes to show/hide machine assignment + combineLatest([ + this.selectedUserForm.get('roles')!.valueChanges.pipe(startWith('')), + this.selectedUserForm.get('assigned_to')!.valueChanges.pipe(startWith('')) + ]).pipe(takeUntil(this._unsubscribeAll)) + .subscribe(([role, clientId]) => { + // Show client assignment dropdown only for Refillers and Admins + this.showClientAssignment = role === 'Refiller' && this.isAdmin; + + // Show machine assignment only when role is Refiller, user is Admin, and client is assigned + this.showMachineAssignment = role === 'Refiller' && this.isAdmin && !!clientId && clientId !== ''; + + if (this.showMachineAssignment && clientId) { + this.loadClientMachines(parseInt(clientId, 10)); + } else { + this.availableMachines = []; + this.selectedMachineIds = []; + } + }); + this.users$ = this._users.asObservable(); this.searchInputControl.valueChanges @@ -143,14 +202,152 @@ export class UserComponent implements OnInit, OnDestroy { this.filterUsers(); }); - this.loadUsers(); + // Load users after a small delay to ensure roles are loaded + setTimeout(() => { + this.loadUsers(); + }, 500); + } + + ngAfterViewInit(): void { + if (this.paginator) { + this.paginator.page + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((page: PageEvent) => { + this.pagination.page = page.pageIndex; + this.pagination.size = page.pageSize; + this.filterUsers(); + }); + } + + if (this.sort) { + this.sort.sortChange + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((sort: Sort) => { + this.sortUsers(sort); + }); + } + } + + ngOnDestroy(): void { + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); } /** - * UPDATED: Load roles from database instead of hardcoded values + * ⭐ NEW: Subscribe to role changes from Role Management component + */ + private subscribeToRoleChanges(): void { + console.log('📢 Subscribing to role changes...'); + + // Listen for role updates (permission changes) + this._roleStateService.roleUpdated$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(update => { + if (update) { + console.log('📢 ROLE UPDATED notification received:', update); + + // Reload roles to get latest permissions + this.loadRolesFromDatabase(); + + // Update users who have this role + this.updateUsersWithRole(update.roleName); + + // Show notification + this.showRoleUpdateNotification(`Role "${update.roleName}" permissions updated`); + } + }); + + // Listen for role deletions + this._roleStateService.roleDeleted$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(roleId => { + if (roleId) { + console.log('📢 ROLE DELETED notification received:', roleId); + + // Reload roles and users + this.loadRolesFromDatabase(); + this.loadUsers(); + + // Show notification + this.showRoleUpdateNotification('A role has been deleted. User list refreshed.'); + } + }); + + // Listen for new roles + this._roleStateService.roleCreated$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(update => { + if (update) { + console.log('📢 ROLE CREATED notification received:', update); + + // Reload roles + this.loadRolesFromDatabase(); + + // Show notification + this.showRoleUpdateNotification(`New role "${update.roleName}" created`); + } + }); + } + + /** + * ⭐ NEW: Update users in the list who have the changed role + */ + private updateUsersWithRole(roleName: string): void { + const users = this._users.getValue(); + let updatedCount = 0; + + // Mark users with this role as needing refresh + const updatedUsers = users.map(user => { + if (user.roles === roleName) { + updatedCount++; + // Flag for UI update - you might want to show an indicator + return { ...user, _roleUpdated: true }; + } + return user; + }); + + this._users.next(updatedUsers); + this.filterUsers(); + + console.log(`✓ Flagged ${updatedCount} users with role: ${roleName} for update`); + } + + /** + * ⭐ NEW: Show notification about role updates + */ + private showRoleUpdateNotification(message: string): void { + console.log('🔔 Role Update:', message); + + // Option 1: Browser notification (if permission granted) + if ('Notification' in window && Notification.permission === 'granted') { + new Notification('Role Management Update', { + body: message, + icon: '/assets/icons/icon-72x72.png' // Adjust path + }); + } + + // Option 2: Console log (always) + console.log('📬', message); + + // Option 3: Show flash message + this.flashMessage = 'success'; + setTimeout(() => { + this.flashMessage = null; + this._changeDetectorRef.markForCheck(); + }, 3000); + + // Option 4: Add a visual indicator in the UI + // You could add a badge or highlight to the role management button + } + + /** + * Load roles from database */ private loadRolesFromDatabase(): void { this.rolesLoading = true; + this.rolesLoaded = false; + + console.log('Loading roles from database...'); this._roleService.getRoles() .pipe(takeUntil(this._unsubscribeAll)) @@ -163,11 +360,15 @@ export class UserComponent implements OnInit, OnDestroy { this.setAvailableRoles(roles); this.rolesLoading = false; + this.rolesLoaded = true; this._changeDetectorRef.markForCheck(); + + console.log('Available roles for dropdown:', this.availableRoles); }, (error) => { console.error('✗ Error loading roles:', error); this.rolesLoading = false; + this.rolesLoaded = true; // Fallback to empty roles if error this.availableRoles = []; @@ -177,7 +378,7 @@ export class UserComponent implements OnInit, OnDestroy { } /** - * UPDATED: Filter roles based on current user's role (from database) + * Filter roles based on current user's role */ private setAvailableRoles(roles: Role[]): void { let filteredRoles: Role[] = []; @@ -202,6 +403,52 @@ export class UserComponent implements OnInit, OnDestroy { console.log('Available roles for dropdown:', this.availableRoles); } + // ⭐ NEW: Load Clients for assignment dropdown + private loadClients(): void { + this.clientsLoading = true; + this._userService.getClients() + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe({ + next: (clients) => { + this.availableClients = clients; + this.clientsLoading = false; + console.log('Loaded clients:', clients); + }, + error: (err) => { + console.error('Error loading clients:', err); + this.clientsLoading = false; + } + }); + } + + // ⭐ NEW: Load machines for selected client + private loadClientMachines(clientId: number): void { + if (!clientId) { + this.availableMachines = []; + return; + } + + this.machinesLoading = true; + this._userService.getClientMachines(clientId) + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe({ + next: (machines) => { + this.availableMachines = machines; + this.machinesLoading = false; + console.log('Loaded machines for client:', machines); + }, + error: (err) => { + console.error('Error loading client machines:', err); + this.machinesLoading = false; + } + }); + } + + // ⭐ NEW: Remove machine ID from selection + removeMachineId(machineId: string): void { + this.selectedMachineIds = this.selectedMachineIds.filter(id => id !== machineId); + } + /** * Refresh roles from database */ @@ -210,29 +457,38 @@ export class UserComponent implements OnInit, OnDestroy { this.loadRolesFromDatabase(); } - ngOnDestroy(): void { - this._unsubscribeAll.next(null); - this._unsubscribeAll.complete(); + /** + * Check if form is ready to submit + */ + isFormReady(): boolean { + return this.selectedUserForm.valid && !this.isLoading && !this.rolesLoading && this.rolesLoaded; } - ngAfterViewInit(): void { - if (this.paginator) { - this.paginator.page - .pipe(takeUntil(this._unsubscribeAll)) - .subscribe((page: PageEvent) => { - this.pagination.page = page.pageIndex; - this.pagination.size = page.pageSize; - this.filterUsers(); + /** + * Debug form validation + */ + debugFormValidation(): void { + console.log('=== FORM VALIDATION DEBUG ==='); + console.log('Form valid:', this.selectedUserForm.valid); + console.log('Form errors:', this.selectedUserForm.errors); + console.log('Roles loaded:', this.rolesLoaded); + console.log('Roles loading:', this.rolesLoading); + console.log('Available roles:', this.availableRoles); + + Object.keys(this.selectedUserForm.controls).forEach(key => { + const control = this.selectedUserForm.get(key); + if (control?.invalid) { + console.log(`❌ ${key}:`, { + errors: control.errors, + value: control.value, + touched: control.touched, + dirty: control.dirty }); - } - - if (this.sort) { - this.sort.sortChange - .pipe(takeUntil(this._unsubscribeAll)) - .subscribe((sort: Sort) => { - this.sortUsers(sort); - }); - } + } else { + console.log(`✓ ${key}:`, control?.value); + } + }); + console.log('=== END DEBUG ==='); } trackByFn(index: number, item: any): any { @@ -336,14 +592,30 @@ export class UserComponent implements OnInit, OnDestroy { email: user.email, contact: user.contact, roles: user.roles, - user_status: user.user_status + user_status: user.user_status, + assigned_to: user.assigned_to || '' }); this.photoPreview = this._userService.getFullFileUrl(user.photo); this.logoPreview = this._userService.getFullFileUrl(user.company_logo); + // Load assigned machines for Refillers + if (user.roles === 'Refiller') { + this.selectedMachineIds = user.assigned_machines?.map((m: any) => m.machine_id) || []; + + // Load available machines if client is assigned + if (user.assigned_to) { + this.loadClientMachines(user.assigned_to); + } + } else { + this.selectedMachineIds = []; + } + + // For existing users, password is optional this.selectedUserForm.get('password')?.clearValidators(); this.selectedUserForm.get('password')?.updateValueAndValidity(); + + this._changeDetectorRef.markForCheck(); } } @@ -359,15 +631,32 @@ export class UserComponent implements OnInit, OnDestroy { this.photoPreview = null; this.logoPreview = null; this.viewingDocument = null; + this.selectedMachineIds = []; + this.selectedUserForm.reset(); + this._changeDetectorRef.markForCheck(); } createUser(): void { + console.log('Creating new user...'); + console.log('Roles loaded:', this.rolesLoaded); + console.log('Available roles:', this.availableRoles); + if (this.selectedUser) { this.closeDetails(); } - // For clients, auto-set role to Refiller - const defaultRole = this.isClient ? 'Refiller' : ''; + // Determine default role + let defaultRole = ''; + + if (this.isClient) { + // Clients can only create Refiller users + defaultRole = 'Refiller'; + console.log('Client creating user - default role: Refiller'); + } else if (this.availableRoles.length > 0) { + // For admins, use first available role as default + defaultRole = this.availableRoles[0].value; + console.log('Admin creating user - default role:', defaultRole); + } const newUser: User = { username: '', @@ -380,17 +669,27 @@ export class UserComponent implements OnInit, OnDestroy { this.selectedUser = newUser; + // Reset form and set defaults this.selectedUserForm.reset(); this.selectedUserForm.patchValue({ user_status: 'Active', - roles: defaultRole + roles: defaultRole, + assigned_to: '' }); + + this.selectedMachineIds = []; + // For new users, password is required this.selectedUserForm.get('password')?.setValidators([Validators.required, Validators.minLength(6)]); this.selectedUserForm.get('password')?.updateValueAndValidity(); + // Scroll to top window.scrollTo({ top: 0, behavior: 'smooth' }); + this._changeDetectorRef.markForCheck(); + + // Debug form state + this.debugFormValidation(); } onPhotoSelected(event: any): void { @@ -520,14 +819,30 @@ export class UserComponent implements OnInit, OnDestroy { } updateSelectedUser(): void { + // Debug form before submission + this.debugFormValidation(); + if (!this.selectedUserForm.valid) { + console.warn('Form is invalid, cannot submit'); + alert('Please fill in all required fields correctly.'); + return; + } + + if (!this.rolesLoaded) { + console.warn('Roles not loaded yet'); + alert('Please wait for roles to load.'); return; } this.isLoading = true; const formValue = this.selectedUserForm.value; + console.log('Submitting user with role:', formValue.roles); + console.log('Assigned to client:', formValue.assigned_to); + console.log('Selected machines:', this.selectedMachineIds); + if (this.selectedUser?.id) { + // Update existing user this._userService.updateUser( this.selectedUser.id, formValue, @@ -538,18 +853,40 @@ export class UserComponent implements OnInit, OnDestroy { .pipe(takeUntil(this._unsubscribeAll)) .subscribe( () => { - this.loadUsers(); - this.showFlashMessage('success'); - this.closeDetails(); - this.isLoading = false; + console.log('✓ User updated successfully'); + + // Update machine assignments if Refiller + if (formValue.roles === 'Refiller' && this.isAdmin && this.selectedMachineIds.length > 0) { + this._userService.updateRefillerMachines(this.selectedUser!.id!, this.selectedMachineIds) + .subscribe({ + next: (machineResponse) => { + console.log('Machines updated:', machineResponse); + this.loadUsers(); + this.showFlashMessage('success'); + this.closeDetails(); + this.isLoading = false; + }, + error: (err) => { + console.error('Error updating machines:', err); + this.showFlashMessage('error'); + this.isLoading = false; + } + }); + } else { + this.loadUsers(); + this.showFlashMessage('success'); + this.closeDetails(); + this.isLoading = false; + } }, (error) => { - console.error('Error updating user:', error); + console.error('✗ Error updating user:', error); this.showFlashMessage('error'); this.isLoading = false; } ); } else { + // Create new user this._userService.addUser( formValue, this.selectedPhoto || undefined, @@ -558,14 +895,35 @@ export class UserComponent implements OnInit, OnDestroy { ) .pipe(takeUntil(this._unsubscribeAll)) .subscribe( - () => { - this.loadUsers(); - this.showFlashMessage('success'); - this.closeDetails(); - this.isLoading = false; + (created) => { + console.log('✓ User created successfully'); + + // Assign machines if Refiller + if (formValue.roles === 'Refiller' && this.isAdmin && this.selectedMachineIds.length > 0) { + this._userService.assignMachinesToRefiller(created.id!, this.selectedMachineIds) + .subscribe({ + next: (machineResponse) => { + console.log('Machines assigned:', machineResponse); + this.loadUsers(); + this.showFlashMessage('success'); + this.closeDetails(); + this.isLoading = false; + }, + error: (err) => { + console.error('Error assigning machines:', err); + this.showFlashMessage('error'); + this.isLoading = false; + } + }); + } else { + this.loadUsers(); + this.showFlashMessage('success'); + this.closeDetails(); + this.isLoading = false; + } }, (error) => { - console.error('Error creating user:', error); + console.error('✗ Error creating user:', error); this.showFlashMessage('error'); this.isLoading = false; } diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.service.ts b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.service.ts index 9ffc7e1..63e72b7 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.service.ts +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/user/user.service.ts @@ -1,4 +1,4 @@ -// user.service.ts - COMPLETE FIXED VERSION +// user.service.ts - WITH OPTION 3 ASSIGNMENT METHODS import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -27,6 +27,21 @@ export interface User { created_at?: string; updated_at?: string; machines?: any[]; + + // ⭐ Created by fields (automatic from backend) + created_by?: number; + created_by_username?: string; + + // ⭐ NEW: Option 3 - Assignment fields + assigned_to?: number | null; + assigned_to_username?: string; + assigned_machines?: Array<{ + machine_id: string; + machine_model: string; + branch_name: string; + operation_status: string; + }>; + assigned_machines_count?: number; } export interface DocumentWithMetadata { @@ -35,6 +50,21 @@ export interface DocumentWithMetadata { documentTypeOther?: string; } +// ⭐ NEW: Client interface for assignment dropdown +export interface Client { + id: number; + username: string; + email: string; +} + +// ⭐ NEW: Machine interface for assignment +export interface Machine { + machine_id: string; + machine_model: string; + branch_name: string; + operation_status: string; +} + @Injectable({ providedIn: 'root' }) @@ -70,6 +100,40 @@ export class UserService { return this.http.get(this.BaseUrl); } + // ⭐ NEW: Get Clients for assignment dropdown + getClients(): Observable { + return this.http.get(`${this.backendBaseUrl}/clients`); + } + + // ⭐ NEW: Get machines for a specific client + getClientMachines(clientId: number): Observable { + return this.http.get(`${this.backendBaseUrl}/clients/${clientId}/machines`); + } + + // ⭐ NEW: Assign machines to Refiller (POST - for new user) + assignMachinesToRefiller(refillerId: number, machineIds: string[]): Observable { + return this.http.post(`${this.backendBaseUrl}/refillers/${refillerId}/machines`, { + machine_ids: machineIds + }); + } + + // ⭐ NEW: Update Refiller machines (PUT - for existing user) + updateRefillerMachines(refillerId: number, machineIds: string[]): Observable { + return this.http.put(`${this.backendBaseUrl}/refillers/${refillerId}/machines`, { + machine_ids: machineIds + }); + } + + // ⭐ NEW: Delete machine assignment + deleteRefillerMachine(refillerId: number, machineId: string): Observable { + return this.http.delete(`${this.backendBaseUrl}/refillers/${refillerId}/machines/${machineId}`); + } + + // ⭐ NEW: Get machines assigned to a Refiller + getRefillerMachines(refillerId: number): Observable { + return this.http.get(`${this.backendBaseUrl}/refillers/${refillerId}/machines`); + } + addUser( user: Partial, photoFile?: File, @@ -77,6 +141,10 @@ export class UserService { documentFiles?: DocumentWithMetadata[] ): Observable { const formData = this.createFormData(user, photoFile, logoFile, documentFiles); + + // ⭐ NOTE: Don't add created_by to FormData + // Backend automatically extracts it from JWT token + return this.http.post(this.BaseUrl, formData); } @@ -89,6 +157,10 @@ export class UserService { ): Observable { const url = `${this.BaseUrl}/${id}`; const formData = this.createFormData(user, photoFile, logoFile, documentFiles); + + // ⭐ NOTE: Don't update created_by + // It's set once during creation and shouldn't change + console.log('PUT Request URL:', url); return this.http.put(url, formData); } @@ -123,6 +195,14 @@ export class UserService { if (user.user_status) formData.append('user_status', user.user_status); if (user.password) formData.append('password', user.password); + // ⭐ NEW: Add assigned_to for client assignment + if (user.assigned_to !== undefined) { + formData.append('assigned_to', user.assigned_to ? user.assigned_to.toString() : ''); + } + + // ⭐ NOTE: created_by is NOT added here + // Backend extracts it from JWT token automatically + // Append photo file if (photoFile) { formData.append('photo', photoFile, photoFile.name); diff --git a/machine-operations/angular.json b/machine-operations/angular.json index 97e1d6a..acc2ed7 100644 --- a/machine-operations/angular.json +++ b/machine-operations/angular.json @@ -103,5 +103,8 @@ } } } + }, + "cli": { + "analytics": "15dead7e-04a1-4f40-8a32-c1cb1a5d23a0" } }

w>q2<>p7fyYj zI4G9s6vCQIjm+l9M8|hgJ%5nLK*(@wG`9(o=AeVhC?l3jd`4jlaF4?Yf~|1=AwB9x zlrSZi+en$jg4BYgd0Wg@c&xFWy(fq1Nk66JXOw(M2?M}g!$Vr~93VAS!=#QnZrdTV z=-KEb>YrpbA=YqWgdmU(u$nMLW^puHvdqytZlK!z#3G&sQH1Q`+hzE6F?3qF!@-kO zTa)tYa>nJaOt0Ckks51{8II}Um=W$rpb|tcW$vAf!sS|Ex!j+8Yp~kb3y@{BK&NzE zemh>II*h`lT43q*0=WEN`Cb9JaX@%gqhK2xO>NvU6{<5sOZ3o^iASeGYp?bvGJjfF zHB(t9fMhzY0=6BlO zLNwRO_~mAE-M#v{d$qMOEw)$7@1OGBcRd(MR{a z_UD9v`5e#;g~)CbYz6^yv0{+e`5<6Nh!udjN@jqeX&v)Go?JCMf>GTr9mo~j$qE%|zq?bg$W=2( z2ed0!1=K?C5HVs0AXzSv1Ml7>uGznich9{!| zY`{OyyGxS}-rX&lC_OT=cL&>Mp}sxkUGTM5yT3$^!Pi{(2=NpxmGf&35Y49omx*4n zfM`+#=vyY9X8-oBwDTgjZ~w6w?eO5G38$|Q&MyO`E$>kB^`Q6 z$K|doRnr^p(Mmdul0LI!Krcaf_y-UbK3qX+%i5JjWR(VUV{MCByHc-Rnb^({P>U~Z zytt9_QF;JFcRl&$3m0CP^0(g!wySoT5m~OmD@=9$o5L4|6Whu0RpX`Hi@8*H71hmr zY-O3zPCeR5$gw-{vQ@j>h^){;D`sjM&6*Cqrh_W2oT+WTRB^F_zX%@S3e1G)NKd}e zhIZ_pY3wu`cfQxS^J>G@C;v;E(Re@IQTKtrm<1qx{n^B`*D9*sTzz5nn;R}{__p6% zv`$~N?($vJi*{cf_^W+C+GpNrjt@ZxLm zptbWic8(pLDr&w~vBa!cqgSlCTy9i!p%r`P3C84=`OqYlhYySyu1@)s$=OabE|cM` zP@gdA(!li`pU?l5q>{hpS(t}B)#Fc9 zs=uYsL6}vP_>M5JKSev@Hz}bNCoDHVr#vH-Y)$zgHDe=DLNY}$IvB5`B6N?E{Bd~G z?-Tj^9h70pR4f(Mv~R{=@cM?=HzYQQiH10rzvt&b(8;@|quZ_w{KdXM-Dh?`sCPf8 zJ#X9i*u2b?6C9DHqqHcMal2?#0?ek{dSBy7OLiW9p-@;X9kDbXZ z_&6;KUQ{g0DP`gOhZ{QAS|_tJ*ILHUG`Q-pPEvN($4aJU3fC*hv2!=lnlnqRg2>Ir zxa*ZV=}`r_*&c3;&9z*we42(F?+$8bOIsY|AcaTp`&E2>?qBjA%B`gfBtpe$Es@MR z{q(70!$;!jbS8rm8YuB06io&Xoj7ppc>9TCCl0lrK6K`EoQzbCFoR*LkGy-uNkCJ-$Po<_7eVjt+&3O9C)L81rDU{wEPVHe=D81RA7E4Pl{TLh3Q%i3i&3HbI eDxmb{=Bx+PQ_DY2q4egHS!YoCMhd0urT;HSI#eeB literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/services/services.py b/Machine-Backend/app/services/services.py index 5bd6dee..ae96aac 100644 --- a/Machine-Backend/app/services/services.py +++ b/Machine-Backend/app/services/services.py @@ -11,7 +11,7 @@ from typing import Optional, Dict, Any import smtplib from email.mime.text import MIMEText from app import db -from app.models.models import Machine, User, Product, VendingSlot, Transaction, Role +from app.models.models import Machine, User, Product, VendingSlot, Transaction, Role, Branch, Brand, Category, SubCategory from flask import current_app import secrets from dotenv import load_dotenv @@ -299,19 +299,19 @@ class MachineService: machine_model=data['machine_model'], machine_type=data['machine_type'], client_id=client_id, - client_name=client_name, # Set client_name from user record + client_name=client_name, branch_id=branch_id, branch_name=data['branch_name'], operation_status=data['operation_status'], connection_status=data['connection_status'], created_on=created_on, - password=password + password=password, + created_by=data.get('created_by') # NEW: Add created_by ) db.session.add(new_machine) db.session.commit() return new_machine - @staticmethod def update_machine(id, data): machine = Machine.query.get_or_404(id) @@ -382,6 +382,98 @@ class MachineService: db.session.commit() return {'success': True, 'message': 'Vending state updated', 'lastUpdated': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")} + @staticmethod + def get_machine_refillers(machine_id): + """ + Get all Refillers assigned to a Machine + + Args: + machine_id: machine_id string + + Returns: + List of User objects (Refillers) + """ + from app.models.models import RefillerMachine, User + + assignments = RefillerMachine.query.filter_by(machine_id=machine_id).all() + + refillers = [] + for assignment in assignments: + refiller = User.query.get(assignment.refiller_id) + if refiller and refiller.roles == 'Refiller': + refillers.append(refiller) + + return refillers + + @staticmethod + def assign_refillers_to_machine(machine_id, refiller_ids, assigned_by_id): + """ + Assign multiple Refillers to a Machine + + Args: + machine_id: machine_id string + refiller_ids: List of Refiller user IDs + assigned_by_id: ID of the user making the assignment + + Returns: + List of created assignments + """ + from app.models.models import RefillerMachine, User, Machine + + print(f"\n{'='*60}") + print(f"ASSIGNING REFILLERS TO MACHINE") + print(f"{'='*60}") + + # Validate machine exists + machine = Machine.query.filter_by(machine_id=machine_id).first() + if not machine: + raise ValueError(f"Machine {machine_id} not found") + + print(f"Machine: {machine_id} ({machine.machine_model})") + print(f"Refillers to assign: {len(refiller_ids)}") + + assignments = [] + + for refiller_id in refiller_ids: + # Validate refiller + refiller = User.query.get(refiller_id) + if not refiller: + print(f" ✗ Refiller {refiller_id} not found - skipping") + continue + if refiller.roles != 'Refiller': + print(f" ✗ User {refiller_id} is not a Refiller - skipping") + continue + + # Check if already assigned + existing = RefillerMachine.query.filter_by( + refiller_id=refiller_id, + machine_id=machine_id + ).first() + + if existing: + print(f" ✓ Refiller {refiller.username} already assigned - skipping") + assignments.append(existing) + continue + + # Create assignment + assignment = RefillerMachine( + refiller_id=refiller_id, + machine_id=machine_id, + assigned_by=assigned_by_id + ) + + db.session.add(assignment) + assignments.append(assignment) + print(f" ✓ Assigned refiller {refiller.username}") + + db.session.commit() + + print(f"\n✓ Successfully assigned {len(assignments)} refillers") + print(f"{'='*60}\n") + + return assignments + + # User Service (MODIFIED FOR BREVO SMTP WITH ENV VARIABLES) # Updated UserService in services.py class UserService: @@ -566,7 +658,7 @@ System Administration Team # Generate user ID only user_id = User.generate_user_id(data['username'], data['email']) - password = data['password'] # Use provided password, not auto-generated + password = data['password'] print(f"Generated User ID: {user_id}") print(f"Password provided by admin") @@ -576,6 +668,17 @@ System Administration Team logo_path = UserService.save_file(logo_file, 'company_logos') if logo_file else None documents = UserService.save_multiple_documents(document_files, documents_metadata) if document_files else [] + # ⭐ NEW: Validate assigned_to if provided + assigned_to = data.get('assigned_to') + if assigned_to: + # Validate that assigned_to user exists and is a Client + client = User.query.get(assigned_to) + if not client: + raise ValueError(f"Client with ID {assigned_to} not found") + if client.roles != 'Client': + raise ValueError(f"User {assigned_to} is not a Client. Can only assign Refillers to Clients.") + print(f"✓ Assigning Refiller to Client: {client.username} (ID: {assigned_to})") + # Create new user new_user = User( user_id=user_id, @@ -586,7 +689,9 @@ System Administration Team user_status=data['user_status'], photo=photo_path, company_logo=logo_path, - documents=json.dumps(documents) + documents=json.dumps(documents), + created_by=data.get('created_by'), + assigned_to=assigned_to # NEW: Set assigned_to ) # Set password (this will hash it automatically) @@ -598,11 +703,10 @@ System Administration Team db.session.commit() print(f"✓ User created successfully with ID: {new_user.id}") + if assigned_to: + print(f"✓ Assigned to Client ID: {assigned_to}") print(f"{'='*60}\n") - # Optional: Send email notification with the password they set - # UserService.send_email_notification(...) - return new_user @staticmethod @@ -634,12 +738,29 @@ System Administration Team user.roles = data.get('roles', user.roles) user.user_status = data.get('user_status', user.user_status) + # ⭐ NEW: Update assigned_to if provided + if 'assigned_to' in data: + new_assigned_to = data['assigned_to'] + + if new_assigned_to: + # Validate that assigned_to user exists and is a Client + client = User.query.get(new_assigned_to) + if not client: + raise ValueError(f"Client with ID {new_assigned_to} not found") + if client.roles != 'Client': + raise ValueError(f"User {new_assigned_to} is not a Client. Can only assign Refillers to Clients.") + print(f"✓ Updating assignment to Client: {client.username} (ID: {new_assigned_to})") + else: + print(f"✓ Removing assignment (set to None)") + + user.assigned_to = new_assigned_to + # Update password if provided (will be hashed automatically) if 'password' in data and data['password']: print(f"Updating password for user") user.set_password(data['password']) - # Handle file updates + # Handle file updates (unchanged) if photo_file: if user.photo: old_path = os.path.join(current_app.config['UPLOAD_FOLDER'], @@ -748,6 +869,173 @@ System Administration Team ] return valid_types.includes(file.type) if hasattr(file, 'type') else False + @staticmethod + def assign_machines_to_refiller(refiller_id, machine_ids, assigned_by_id): + """ + Assign multiple machines to a Refiller + + Args: + refiller_id: ID of the Refiller user + machine_ids: List of machine_id strings + assigned_by_id: ID of the user making the assignment (Admin) + + Returns: + List of created RefillerMachine assignments + """ + from app.models.models import RefillerMachine, User, Machine + + print(f"\n{'='*60}") + print(f"ASSIGNING MACHINES TO REFILLER") + print(f"{'='*60}") + + # Validate refiller exists and is actually a Refiller + refiller = User.query.get(refiller_id) + if not refiller: + raise ValueError(f"Refiller with ID {refiller_id} not found") + if refiller.roles != 'Refiller': + raise ValueError(f"User {refiller_id} is not a Refiller") + + print(f"Refiller: {refiller.username} (ID: {refiller_id})") + print(f"Machines to assign: {len(machine_ids)}") + + # Optional: Validate machines belong to assigned client + if refiller.assigned_to: + client = User.query.get(refiller.assigned_to) + print(f"Assigned to Client: {client.username if client else 'None'}") + + assignments = [] + + for machine_id in machine_ids: + # Validate machine exists + machine = Machine.query.filter_by(machine_id=machine_id).first() + if not machine: + print(f" ✗ Machine {machine_id} not found - skipping") + continue + + # Optional: Check if machine belongs to assigned client + if refiller.assigned_to and machine.client_id != refiller.assigned_to: + print(f" ⚠ Warning: Machine {machine_id} doesn't belong to assigned client") + + # Check if assignment already exists + existing = RefillerMachine.query.filter_by( + refiller_id=refiller_id, + machine_id=machine_id + ).first() + + if existing: + print(f" ✓ Machine {machine_id} already assigned - skipping") + assignments.append(existing) + continue + + # Create new assignment + assignment = RefillerMachine( + refiller_id=refiller_id, + machine_id=machine_id, + assigned_by=assigned_by_id + ) + + db.session.add(assignment) + assignments.append(assignment) + print(f" ✓ Assigned machine {machine_id} ({machine.machine_model})") + + db.session.commit() + + print(f"\n✓ Successfully assigned {len(assignments)} machines") + print(f"{'='*60}\n") + + return assignments + + @staticmethod + def remove_machine_from_refiller(refiller_id, machine_id): + """ + Remove a machine assignment from a Refiller + + Args: + refiller_id: ID of the Refiller user + machine_id: machine_id string to remove + + Returns: + True if removed, False if not found + """ + from app.models.models import RefillerMachine + + assignment = RefillerMachine.query.filter_by( + refiller_id=refiller_id, + machine_id=machine_id + ).first() + + if not assignment: + return False + + db.session.delete(assignment) + db.session.commit() + + print(f"✓ Removed machine {machine_id} from refiller {refiller_id}") + + return True + + @staticmethod + def get_refiller_machines(refiller_id): + """ + Get all machines assigned to a Refiller + + Args: + refiller_id: ID of the Refiller user + + Returns: + List of Machine objects + """ + from app.models.models import RefillerMachine, Machine + + assignments = RefillerMachine.query.filter_by(refiller_id=refiller_id).all() + + machines = [] + for assignment in assignments: + machine = Machine.query.filter_by(machine_id=assignment.machine_id).first() + if machine: + machines.append(machine) + + return machines + + @staticmethod + def update_refiller_machines(refiller_id, machine_ids, assigned_by_id): + """ + Update machine assignments for a Refiller (replaces all existing) + + Args: + refiller_id: ID of the Refiller user + machine_ids: List of machine_id strings (new complete list) + assigned_by_id: ID of the user making the assignment + + Returns: + List of updated assignments + """ + from app.models.models import RefillerMachine + + print(f"\n{'='*60}") + print(f"UPDATING REFILLER MACHINE ASSIGNMENTS") + print(f"{'='*60}") + print(f"Refiller ID: {refiller_id}") + print(f"New machine list: {machine_ids}") + + # Remove all existing assignments + RefillerMachine.query.filter_by(refiller_id=refiller_id).delete() + db.session.commit() + print("✓ Cleared existing assignments") + + # Add new assignments + if machine_ids and len(machine_ids) > 0: + assignments = UserService.assign_machines_to_refiller( + refiller_id, + machine_ids, + assigned_by_id + ) + return assignments + else: + print("✓ No machines to assign") + return [] + + # Product Service (UNCHANGED) class ProductService: @staticmethod @@ -793,12 +1081,30 @@ class ProductService: product_id = ProductService.generate_id("P") created_date = data.get('created_date', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + # Parse billing and expiration dates + billing_date = None + if data.get('billing_date'): + try: + billing_date = datetime.datetime.strptime(data['billing_date'], "%Y-%m-%d") + except ValueError: + pass + + expiration_date = None + if data.get('expiration_date'): + try: + expiration_date = datetime.datetime.strptime(data['expiration_date'], "%Y-%m-%d") + except ValueError: + pass + new_product = Product( product_id=product_id, product_name=data['product_name'], price=float(data['price']), product_image=image_path, - created_date=created_date + created_date=created_date, + billing_date=billing_date, + expiration_date=expiration_date, + created_by=data.get('created_by') ) db.session.add(new_product) @@ -815,6 +1121,20 @@ class ProductService: product.price = float(data.get('price', product.price)) product.created_date = data.get('created_date', product.created_date) + # Update billing date + if 'billing_date' in data and data['billing_date']: + try: + product.billing_date = datetime.datetime.strptime(data['billing_date'], "%Y-%m-%d") + except ValueError: + pass + + # Update expiration date + if 'expiration_date' in data and data['expiration_date']: + try: + product.expiration_date = datetime.datetime.strptime(data['expiration_date'], "%Y-%m-%d") + except ValueError: + pass + if file: if product.product_image: old_image_path = os.path.join(current_app.config['UPLOAD_FOLDER'], product.product_image.split('/')[-1]) @@ -920,14 +1240,15 @@ class RoleService: new_role = Role( name=name, description=description, - permissions=json.dumps(permissions) + permissions=json.dumps(permissions), + created_by=data.get('created_by') # NEW: Add created_by ) db.session.add(new_role) db.session.commit() return new_role - + @staticmethod def update_role(role_id, data): """Update existing role""" @@ -964,4 +1285,685 @@ class RoleService: db.session.delete(role) db.session.commit() + return True + +class BranchService: + """Service for managing branches""" + + @staticmethod + def validate_branch_data(data): + """Validate branch data""" + import re + + required_fields = ['code', 'name', 'location', 'address', 'contact'] + for field in required_fields: + if field not in data or not data[field]: + raise ValueError(f"Missing required field: {field}") + + # Validate code format (uppercase alphanumeric) + if not re.match(r'^[A-Z0-9]+$', data['code']): + raise ValueError("Branch code must contain only uppercase letters and numbers") + + # Validate contact (10 digits) + if not re.match(r'^\d{10}$', data['contact']): + raise ValueError("Contact must be exactly 10 digits") + + # Validate name length + if len(data['name']) < 3: + raise ValueError("Branch name must be at least 3 characters long") + + # Validate address length + if len(data['address']) < 10: + raise ValueError("Address must be at least 10 characters long") + + @staticmethod + def get_all_branches(): + """Get all branches""" + from app.models.models import Branch + return Branch.query.order_by(Branch.created_at.desc()).all() + + @staticmethod + def get_branch_by_id(branch_id): + """Get branch by ID""" + from app.models.models import Branch + return Branch.query.get_or_404(branch_id) + + @staticmethod + def create_branch(data): + """Create new branch""" + from app.models.models import Branch + from app import db + + # Validate data + BranchService.validate_branch_data(data) + + # Check if code already exists + existing = Branch.query.filter_by(code=data['code']).first() + if existing: + raise ValueError(f"Branch code '{data['code']}' already exists") + + # Generate unique branch ID + branch_id = Branch.generate_branch_id() + + # Create new branch + new_branch = Branch( + branch_id=branch_id, + code=data['code'].upper(), + name=data['name'], + location=data['location'], + address=data['address'], + contact=data['contact'], + created_by=data.get('created_by') # NEW: Add created_by + ) + + db.session.add(new_branch) + db.session.commit() + + print(f"✓ Branch created: {new_branch.name} ({new_branch.branch_id})") + + return new_branch + + @staticmethod + def update_branch(branch_id, data): + """Update existing branch""" + from app.models.models import Branch + from app import db + + branch = Branch.query.get_or_404(branch_id) + + # Validate data + BranchService.validate_branch_data(data) + + # Check if new code conflicts with existing branch + if data['code'] != branch.code: + existing = Branch.query.filter( + Branch.code == data['code'], + Branch.id != branch_id + ).first() + if existing: + raise ValueError(f"Branch code '{data['code']}' already exists") + + # Update fields + branch.code = data['code'].upper() + branch.name = data['name'] + branch.location = data['location'] + branch.address = data['address'] + branch.contact = data['contact'] + branch.updated_at = datetime.datetime.utcnow() + + db.session.commit() + + print(f"✓ Branch updated: {branch.name} ({branch.branch_id})") + + return branch + + @staticmethod + def delete_branch(branch_id): + """Delete branch""" + from app.models.models import Branch + from app import db + + branch = Branch.query.get_or_404(branch_id) + + # TODO: Check if branch is associated with any machines + # This prevents deleting branches that are in use + + branch_name = branch.name + + db.session.delete(branch) + db.session.commit() + + print(f"✓ Branch deleted: {branch_name}") + + return True + + @staticmethod + def search_branches(query): + """Search branches by name, code, or location""" + from app.models.models import Branch + + search_term = f"%{query}%" + + branches = Branch.query.filter( + db.or_( + Branch.name.ilike(search_term), + Branch.code.ilike(search_term), + Branch.location.ilike(search_term), + Branch.branch_id.ilike(search_term) + ) + ).order_by(Branch.created_at.desc()).all() + + return branches + +class BrandService: + """Service for managing brands""" + + @staticmethod + def validate_brand_data(data): + """Validate brand data""" + required_fields = ['name', 'branch_id'] + for field in required_fields: + if field not in data or not data[field]: + raise ValueError(f"Missing required field: {field}") + + # Validate name length + if len(data['name']) < 2: + raise ValueError("Brand name must be at least 2 characters long") + + @staticmethod + def save_image(file): + """Save brand image and return path""" + if not file: + return None + + filename = f"{uuid.uuid4().hex}_{file.filename}" + upload_folder = os.path.join(current_app.config['UPLOAD_FOLDER'], 'brand_images') + + os.makedirs(upload_folder, exist_ok=True) + + file_path = os.path.join(upload_folder, filename) + file.save(file_path) + + return f"/uploads/brand_images/{filename}" + + @staticmethod + def get_all_brands(): + """Get all brands with branch information""" + from app.models.models import Brand + return Brand.query.order_by(Brand.created_at.desc()).all() + + @staticmethod + def get_brand_by_id(brand_id): + """Get brand by ID""" + from app.models.models import Brand + return Brand.query.get_or_404(brand_id) + + @staticmethod + def create_brand(data, image_file=None): + """Create new brand""" + from app.models.models import Brand, Branch + from app import db + + print(f"\n{'='*60}") + print(f"CREATING NEW BRAND") + print(f"{'='*60}") + + # Validate data + BrandService.validate_brand_data(data) + + # Get branch information + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError(f"Branch not found: {data['branch_id']}") + + # Generate brand ID from name + brand_id = Brand.generate_brand_id(data['name']) + print(f"Generated Brand ID: {brand_id}") + + # Handle image upload + image_path = BrandService.save_image(image_file) if image_file else None + + # Create new brand + new_brand = Brand( + brand_id=brand_id, + name=data['name'], + branch_id=branch.branch_id, + branch_name=branch.name, + image=image_path, + created_by=data.get('created_by') # NEW: Add created_by + ) + + db.session.add(new_brand) + db.session.commit() + + print(f"✓ Brand created: {new_brand.name} ({new_brand.brand_id})") + print(f"✓ Linked to branch: {branch.name}") + print(f"{'='*60}\n") + + return new_brand + + @staticmethod + def update_brand(brand_id, data, image_file=None): + """Update existing brand""" + from app.models.models import Brand, Branch + from app import db + + print(f"\n{'='*60}") + print(f"UPDATING BRAND - ID: {brand_id}") + print(f"{'='*60}") + + brand = Brand.query.get_or_404(brand_id) + + # Validate data + BrandService.validate_brand_data(data) + + # Get branch information if branch changed + if data['branch_id'] != brand.branch_id: + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError(f"Branch not found: {data['branch_id']}") + brand.branch_id = branch.branch_id + brand.branch_name = branch.name + + # Update name and regenerate brand_id if name changed + if data['name'] != brand.name: + old_brand_id = brand.brand_id + brand.name = data['name'] + brand.brand_id = Brand.generate_brand_id(data['name']) + print(f"Brand ID changed: {old_brand_id} -> {brand.brand_id}") + + # Handle image update + if image_file: + # Delete old image if exists + if brand.image: + old_image_path = os.path.join( + current_app.config['UPLOAD_FOLDER'], + brand.image.replace('/uploads/', '') + ) + if os.path.exists(old_image_path): + os.remove(old_image_path) + + brand.image = BrandService.save_image(image_file) + + brand.updated_at = datetime.datetime.utcnow() + + db.session.commit() + + print(f"✓ Brand updated: {brand.name} ({brand.brand_id})") + print(f"{'='*60}\n") + + return brand + + @staticmethod + def delete_brand(brand_id): + """Delete brand""" + from app.models.models import Brand + from app import db + + brand = Brand.query.get_or_404(brand_id) + + brand_name = brand.name + + # Delete image if exists + if brand.image: + image_path = os.path.join( + current_app.config['UPLOAD_FOLDER'], + brand.image.replace('/uploads/', '') + ) + if os.path.exists(image_path): + os.remove(image_path) + + db.session.delete(brand) + db.session.commit() + + print(f"✓ Brand deleted: {brand_name}") + + return True + + @staticmethod + def search_brands(query): + """Search brands by name or brand_id""" + from app.models.models import Brand + + search_term = f"%{query}%" + + brands = Brand.query.filter( + db.or_( + Brand.name.ilike(search_term), + Brand.brand_id.ilike(search_term), + Brand.branch_name.ilike(search_term) + ) + ).order_by(Brand.created_at.desc()).all() + + return brands + + @staticmethod + def get_brands_by_branch(branch_id): + """Get all brands for a specific branch""" + from app.models.models import Brand + + brands = Brand.query.filter_by(branch_id=branch_id)\ + .order_by(Brand.created_at.desc()).all() + + return brands + +class CategoryService: + """Service for managing categories""" + + @staticmethod + def validate_category_data(data): + """Validate category data""" + required_fields = ['name', 'brand_id', 'branch_id'] + for field in required_fields: + if field not in data or not data[field]: + raise ValueError(f"Missing required field: {field}") + + # Validate name length + if len(data['name']) < 2: + raise ValueError("Category name must be at least 2 characters long") + + @staticmethod + def save_image(file): + """Save category image and return path""" + if not file: + return None + + filename = f"{uuid.uuid4().hex}_{file.filename}" + upload_folder = os.path.join(current_app.config['UPLOAD_FOLDER'], 'category_images') + + os.makedirs(upload_folder, exist_ok=True) + + file_path = os.path.join(upload_folder, filename) + file.save(file_path) + + return f"/uploads/category_images/{filename}" + + @staticmethod + def get_all_categories(): + """Get all categories with related information""" + from app.models.models import Category + return Category.query.order_by(Category.created_at.desc()).all() + + @staticmethod + def get_category_by_id(category_id): + """Get category by ID""" + from app.models.models import Category + return Category.query.get_or_404(category_id) + + @staticmethod + def create_category(data, image_file=None): + """Create new category""" + from app.models.models import Category, Brand, Branch + from app import db + + print(f"\n{'='*60}") + print(f"CREATING NEW CATEGORY") + print(f"{'='*60}") + + # Validate data + CategoryService.validate_category_data(data) + + # Get brand information + brand = Brand.query.filter_by(brand_id=data['brand_id']).first() + if not brand: + raise ValueError(f"Brand not found: {data['brand_id']}") + + # Get branch information + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError(f"Branch not found: {data['branch_id']}") + + # Generate category ID from name + category_id = Category.generate_category_id(data['name']) + print(f"Generated Category ID: {category_id}") + + # Handle image upload + image_path = CategoryService.save_image(image_file) if image_file else None + + # Create new category + new_category = Category( + category_id=category_id, + name=data['name'], + brand_id=brand.brand_id, + brand_name=brand.name, + branch_id=branch.branch_id, + branch_name=branch.name, + image=image_path, + created_by=data.get('created_by') # NEW: Add created_by + ) + + db.session.add(new_category) + db.session.commit() + + print(f"✓ Category created: {new_category.name} ({new_category.category_id})") + print(f"✓ Linked to brand: {brand.name}") + print(f"✓ Linked to branch: {branch.name}") + print(f"{'='*60}\n") + + return new_category + + @staticmethod + def update_category(category_id, data, image_file=None): + """Update existing category""" + from app.models.models import Category, Brand, Branch + from app import db + + print(f"\n{'='*60}") + print(f"UPDATING CATEGORY - ID: {category_id}") + print(f"{'='*60}") + + category = Category.query.get_or_404(category_id) + + # Validate data + CategoryService.validate_category_data(data) + + # Get brand information if brand changed + if data['brand_id'] != category.brand_id: + brand = Brand.query.filter_by(brand_id=data['brand_id']).first() + if not brand: + raise ValueError(f"Brand not found: {data['brand_id']}") + category.brand_id = brand.brand_id + category.brand_name = brand.name + + # Get branch information if branch changed + if data['branch_id'] != category.branch_id: + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError(f"Branch not found: {data['branch_id']}") + category.branch_id = branch.branch_id + category.branch_name = branch.name + + # Update name and regenerate category_id if name changed + if data['name'] != category.name: + old_category_id = category.category_id + category.name = data['name'] + category.category_id = Category.generate_category_id(data['name']) + print(f"Category ID changed: {old_category_id} -> {category.category_id}") + + # Handle image update + if image_file: + # Delete old image if exists + if category.image: + old_image_path = os.path.join( + current_app.config['UPLOAD_FOLDER'], + category.image.replace('/uploads/', '') + ) + if os.path.exists(old_image_path): + os.remove(old_image_path) + + category.image = CategoryService.save_image(image_file) + + category.updated_at = datetime.datetime.utcnow() + + db.session.commit() + + print(f"✓ Category updated: {category.name} ({category.category_id})") + print(f"{'='*60}\n") + + return category + + @staticmethod + def delete_category(category_id): + """Delete category""" + from app.models.models import Category + from app import db + + category = Category.query.get_or_404(category_id) + + category_name = category.name + + # Delete image if exists + if category.image: + image_path = os.path.join( + current_app.config['UPLOAD_FOLDER'], + category.image.replace('/uploads/', '') + ) + if os.path.exists(image_path): + os.remove(image_path) + + db.session.delete(category) + db.session.commit() + + print(f"✓ Category deleted: {category_name}") + + return True + + @staticmethod + def search_categories(query): + """Search categories by name or category_id""" + from app.models.models import Category + + search_term = f"%{query}%" + + categories = Category.query.filter( + db.or_( + Category.name.ilike(search_term), + Category.category_id.ilike(search_term), + Category.brand_name.ilike(search_term), + Category.branch_name.ilike(search_term) + ) + ).order_by(Category.created_at.desc()).all() + + return categories + + @staticmethod + def get_categories_by_brand(brand_id): + """Get all categories for a specific brand""" + from app.models.models import Category + + categories = Category.query.filter_by(brand_id=brand_id)\ + .order_by(Category.created_at.desc()).all() + + return categories + + @staticmethod + def get_categories_by_branch(branch_id): + """Get all categories for a specific branch""" + from app.models.models import Category + + categories = Category.query.filter_by(branch_id=branch_id)\ + .order_by(Category.created_at.desc()).all() + + return categories + +class SubCategoryService: + @staticmethod + def validate_data(data): + required = ['name', 'category_id', 'brand_id', 'branch_id'] + for field in required: + if field not in data or not data[field]: + raise ValueError(f"Missing required field: {field}") + if len(data['name']) < 2: + raise ValueError("Name must be at least 2 characters") + + @staticmethod + def save_image(file): + if not file: + return None + filename = f"{uuid.uuid4().hex}_{file.filename}" + folder = os.path.join(current_app.config['UPLOAD_FOLDER'], 'subcategory_images') + os.makedirs(folder, exist_ok=True) + file.save(os.path.join(folder, filename)) + return f"/uploads/subcategory_images/{filename}" + + @staticmethod + def get_all(): + from app.models.models import SubCategory + return SubCategory.query.order_by(SubCategory.created_at.desc()).all() + + @staticmethod + def get_by_id(id): + from app.models.models import SubCategory + return SubCategory.query.get_or_404(id) + + @staticmethod + def create(data, image_file=None): + from app.models.models import SubCategory, Category, Brand, Branch + from app import db + + SubCategoryService.validate_data(data) + + category = Category.query.filter_by(category_id=data['category_id']).first() + if not category: + raise ValueError(f"Category not found") + + brand = Brand.query.filter_by(brand_id=data['brand_id']).first() + if not brand: + raise ValueError(f"Brand not found") + + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError(f"Branch not found") + + sub_id = SubCategory.generate_sub_category_id(data['name']) + image = SubCategoryService.save_image(image_file) if image_file else None + + new_sub = SubCategory( + sub_category_id=sub_id, + name=data['name'], + image=image, + category_id=category.category_id, + category_name=category.name, + brand_id=brand.brand_id, + brand_name=brand.name, + branch_id=branch.branch_id, + branch_name=branch.name, + created_by=data.get('created_by') # NEW: Add created_by + ) + + db.session.add(new_sub) + db.session.commit() + + print(f"✓ SubCategory created: {new_sub.name} ({new_sub.sub_category_id})") + + return new_sub + + @staticmethod + def update(id, data, image_file=None): + from app.models.models import SubCategory, Category, Brand, Branch + from app import db + subcat = SubCategory.query.get_or_404(id) + SubCategoryService.validate_data(data) + if data['category_id'] != subcat.category_id: + category = Category.query.filter_by(category_id=data['category_id']).first() + if not category: + raise ValueError("Category not found") + subcat.category_id = category.category_id + subcat.category_name = category.name + if data['brand_id'] != subcat.brand_id: + brand = Brand.query.filter_by(brand_id=data['brand_id']).first() + if not brand: + raise ValueError("Brand not found") + subcat.brand_id = brand.brand_id + subcat.brand_name = brand.name + if data['branch_id'] != subcat.branch_id: + branch = Branch.query.filter_by(branch_id=data['branch_id']).first() + if not branch: + raise ValueError("Branch not found") + subcat.branch_id = branch.branch_id + subcat.branch_name = branch.name + if data['name'] != subcat.name: + subcat.name = data['name'] + subcat.sub_category_id = SubCategory.generate_sub_category_id(data['name']) + if image_file: + if subcat.image: + old_path = os.path.join(current_app.config['UPLOAD_FOLDER'], subcat.image.replace('/uploads/', '')) + if os.path.exists(old_path): + os.remove(old_path) + subcat.image = SubCategoryService.save_image(image_file) + subcat.updated_at = datetime.datetime.utcnow() + db.session.commit() + return subcat + + @staticmethod + def delete(id): + from app.models.models import SubCategory + from app import db + subcat = SubCategory.query.get_or_404(id) + if subcat.image: + path = os.path.join(current_app.config['UPLOAD_FOLDER'], subcat.image.replace('/uploads/', '')) + if os.path.exists(path): + os.remove(path) + db.session.delete(subcat) + db.session.commit() return True \ No newline at end of file diff --git a/Machine-Backend/app/uploads/04a721feffc64a119d22391e9ba31514_7up.jpg b/Machine-Backend/app/uploads/04a721feffc64a119d22391e9ba31514_7up.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd60a242ef17369f65acb2ed17d55ae82b66ff13 GIT binary patch literal 148568 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 Sf-2x!p@;q-+#392?0*23ZTji} literal 0 HcmV?d00001 diff --git a/Machine-Backend/app/uploads/a054b74e7a5c4714bf8199c43c8e58e2_7up.png b/Machine-Backend/app/uploads/a054b74e7a5c4714bf8199c43c8e58e2_7up.png deleted file mode 100644 index b153a932647b0c73f7bfc9b49ac14cdfd1ae1242..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55253 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 literal 0 HcmV?d00001 diff --git a/Machine-Backend/backup.sql b/Machine-Backend/backup.sql new file mode 100644 index 0000000..e69de29 diff --git a/Machine-Backend/backup_db.py b/Machine-Backend/backup_db.py new file mode 100644 index 0000000..2bb9244 --- /dev/null +++ b/Machine-Backend/backup_db.py @@ -0,0 +1,255 @@ +""" +SQLite Database Backup Script +Usage: python backup_db.py [backup|restore|list] +""" + +import os +import shutil +import sqlite3 +from datetime import datetime +from pathlib import Path + +# Configuration +INSTANCE_DIR = Path(__file__).parent / 'instance' +DB_FILE = INSTANCE_DIR / 'machines.db' +BACKUP_DIR = INSTANCE_DIR / 'backups' + + +def ensure_backup_dir(): + """Create backup directory if it doesn't exist""" + BACKUP_DIR.mkdir(parents=True, exist_ok=True) + + +def backup_database(): + """Create a timestamped backup of the database""" + if not DB_FILE.exists(): + print(f"❌ Database not found: {DB_FILE}") + return False + + ensure_backup_dir() + + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + backup_file = BACKUP_DIR / f'machines_backup_{timestamp}.db' + + try: + # Method 1: Simple file copy (faster) + shutil.copy2(DB_FILE, backup_file) + + # Get file size + size = backup_file.stat().st_size + size_mb = size / (1024 * 1024) + + print(f"✅ Backup created successfully!") + print(f" File: {backup_file.name}") + print(f" Size: {size_mb:.2f} MB") + print(f" Location: {backup_file}") + + return True + + except Exception as e: + print(f"❌ Backup failed: {e}") + return False + + +def backup_database_with_integrity(): + """Create a backup using SQLite's built-in backup API (slower but safer)""" + if not DB_FILE.exists(): + print(f"❌ Database not found: {DB_FILE}") + return False + + ensure_backup_dir() + + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + backup_file = BACKUP_DIR / f'machines_backup_{timestamp}.db' + + try: + # Connect to source database + source_conn = sqlite3.connect(str(DB_FILE)) + + # Connect to backup database + backup_conn = sqlite3.connect(str(backup_file)) + + # Perform backup + with backup_conn: + source_conn.backup(backup_conn) + + source_conn.close() + backup_conn.close() + + # Get file size + size = backup_file.stat().st_size + size_mb = size / (1024 * 1024) + + print(f"✅ Backup created successfully (with integrity check)!") + print(f" File: {backup_file.name}") + print(f" Size: {size_mb:.2f} MB") + print(f" Location: {backup_file}") + + return True + + except Exception as e: + print(f"❌ Backup failed: {e}") + if backup_file.exists(): + backup_file.unlink() # Delete partial backup + return False + + +def list_backups(): + """List all available backups""" + ensure_backup_dir() + + backups = sorted(BACKUP_DIR.glob('machines_backup_*.db'), reverse=True) + + if not backups: + print("📂 No backups found") + return + + print(f"\n📂 Available Backups ({len(backups)} total):") + print("=" * 80) + + for i, backup in enumerate(backups, 1): + size = backup.stat().st_size / (1024 * 1024) + mtime = datetime.fromtimestamp(backup.stat().st_mtime) + + # Parse timestamp from filename + try: + parts = backup.stem.split('_') + date_str = parts[-2] + time_str = parts[-1] + backup_date = datetime.strptime(f"{date_str}_{time_str}", "%Y%m%d_%H%M%S") + date_display = backup_date.strftime("%Y-%m-%d %H:%M:%S") + except: + date_display = mtime.strftime("%Y-%m-%d %H:%M:%S") + + print(f"{i:2d}. {backup.name}") + print(f" Date: {date_display}") + print(f" Size: {size:.2f} MB") + print() + + +def restore_database(backup_name=None): + """Restore database from a backup""" + ensure_backup_dir() + + backups = sorted(BACKUP_DIR.glob('machines_backup_*.db'), reverse=True) + + if not backups: + print("❌ No backups found") + return False + + # If no backup specified, show list and ask + if not backup_name: + list_backups() + print("=" * 80) + choice = input("Enter backup number to restore (or 'q' to quit): ").strip() + + if choice.lower() == 'q': + print("❌ Restore cancelled") + return False + + try: + index = int(choice) - 1 + if index < 0 or index >= len(backups): + print("❌ Invalid backup number") + return False + backup_file = backups[index] + except ValueError: + print("❌ Invalid input") + return False + else: + # Find backup by name + backup_file = BACKUP_DIR / backup_name + if not backup_file.exists(): + print(f"❌ Backup not found: {backup_name}") + return False + + # Confirm restore + print(f"\n⚠️ WARNING: This will replace your current database!") + print(f" Current: {DB_FILE}") + print(f" Backup: {backup_file.name}") + confirm = input("\nType 'yes' to confirm restore: ").strip().lower() + + if confirm != 'yes': + print("❌ Restore cancelled") + return False + + try: + # Create a safety backup of current database + if DB_FILE.exists(): + safety_backup = BACKUP_DIR / f'machines_before_restore_{datetime.now().strftime("%Y%m%d_%H%M%S")}.db' + shutil.copy2(DB_FILE, safety_backup) + print(f"✅ Safety backup created: {safety_backup.name}") + + # Restore from backup + shutil.copy2(backup_file, DB_FILE) + + print(f"✅ Database restored successfully!") + print(f" Restored from: {backup_file.name}") + print(f"\n⚠️ Remember to restart your Flask server!") + + return True + + except Exception as e: + print(f"❌ Restore failed: {e}") + return False + + +def cleanup_old_backups(keep_count=10): + """Keep only the most recent N backups""" + ensure_backup_dir() + + backups = sorted(BACKUP_DIR.glob('machines_backup_*.db'), reverse=True) + + if len(backups) <= keep_count: + print(f"✅ No cleanup needed (found {len(backups)} backups, keeping {keep_count})") + return + + to_delete = backups[keep_count:] + + print(f"\n🗑️ Cleanup: Keeping {keep_count} most recent backups") + print(f" Deleting {len(to_delete)} old backups...") + + for backup in to_delete: + try: + backup.unlink() + print(f" ✓ Deleted: {backup.name}") + except Exception as e: + print(f" ✗ Failed to delete {backup.name}: {e}") + + print(f"✅ Cleanup complete!") + + +def main(): + """Main entry point""" + import sys + + if len(sys.argv) < 2: + print("Usage: python backup_db.py [backup|restore|list|cleanup]") + print("\nCommands:") + print(" backup - Create a new backup") + print(" restore - Restore from a backup") + print(" list - List all available backups") + print(" cleanup - Delete old backups (keep 10 most recent)") + return + + command = sys.argv[1].lower() + + if command == 'backup': + backup_database() + elif command == 'backup-safe': + backup_database_with_integrity() + elif command == 'restore': + backup_name = sys.argv[2] if len(sys.argv) > 2 else None + restore_database(backup_name) + elif command == 'list': + list_backups() + elif command == 'cleanup': + keep = int(sys.argv[2]) if len(sys.argv) > 2 else 10 + cleanup_old_backups(keep) + else: + print(f"❌ Unknown command: {command}") + print(" Use: backup, restore, list, or cleanup") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Machine-Backend/migration_sqlite.py b/Machine-Backend/migration_sqlite.py new file mode 100644 index 0000000..ba3f643 --- /dev/null +++ b/Machine-Backend/migration_sqlite.py @@ -0,0 +1,603 @@ +""" +Complete Migration Script with Integrated Backup & Restore +Option 3: Client + Machine Assignment + +FLOW: +1. Backup current database +2. Run migration +3. Verify migration success +4. On failure: Auto-restore from backup + +Usage: python migrate_with_backup.py + +Author: System +Date: 2025-01-25 +Version: 2.0.0 +""" + +import sys +import os +import shutil +from datetime import datetime +from pathlib import Path + +# Add parent directory to path +current_dir = os.path.dirname(os.path.abspath(__file__)) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) + + +class DatabaseBackupManager: + """Handles database backup and restore operations""" + + def __init__(self, db_path): + self.db_path = Path(db_path) + self.backup_dir = self.db_path.parent / 'backups' + self.current_backup = None + + def ensure_backup_dir(self): + """Create backup directory if it doesn't exist""" + self.backup_dir.mkdir(parents=True, exist_ok=True) + + def create_backup(self): + """Create timestamped backup of database""" + if not self.db_path.exists(): + raise FileNotFoundError(f"Database not found: {self.db_path}") + + self.ensure_backup_dir() + + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + backup_file = self.backup_dir / f'machines_backup_{timestamp}.db' + + try: + # Create backup + shutil.copy2(self.db_path, backup_file) + self.current_backup = backup_file + + # Get file size + size = backup_file.stat().st_size / (1024 * 1024) + + print(f"✅ Backup created successfully!") + print(f" File: {backup_file.name}") + print(f" Size: {size:.2f} MB") + print(f" Location: {backup_file}") + + return backup_file + + except Exception as e: + raise Exception(f"Backup failed: {e}") + + def restore_backup(self, backup_file=None): + """Restore database from backup""" + if backup_file is None: + backup_file = self.current_backup + + if backup_file is None: + raise ValueError("No backup file specified") + + if not backup_file.exists(): + raise FileNotFoundError(f"Backup file not found: {backup_file}") + + try: + # Create safety backup of current database + if self.db_path.exists(): + safety_backup = self.backup_dir / f'machines_before_restore_{datetime.now().strftime("%Y%m%d_%H%M%S")}.db' + shutil.copy2(self.db_path, safety_backup) + print(f"✅ Safety backup created: {safety_backup.name}") + + # Restore from backup + shutil.copy2(backup_file, self.db_path) + + print(f"✅ Database restored successfully!") + print(f" Restored from: {backup_file.name}") + + return True + + except Exception as e: + raise Exception(f"Restore failed: {e}") + + def list_backups(self): + """List all available backups""" + self.ensure_backup_dir() + backups = sorted(self.backup_dir.glob('machines_backup_*.db'), reverse=True) + return backups + + def cleanup_old_backups(self, keep_count=10): + """Keep only the most recent N backups""" + backups = self.list_backups() + + if len(backups) <= keep_count: + print(f"✅ Cleanup: Keeping all {len(backups)} backups") + return + + to_delete = backups[keep_count:] + + print(f"🗑️ Cleanup: Keeping {keep_count} most recent backups") + print(f" Deleting {len(to_delete)} old backups...") + + for backup in to_delete: + try: + backup.unlink() + print(f" ✓ Deleted: {backup.name}") + except Exception as e: + print(f" ✗ Failed to delete {backup.name}: {e}") + + +class MigrationError(Exception): + """Custom exception for migration errors""" + pass + + +class Option3MigrationWithBackup: + """Complete migration with integrated backup and restore""" + + def __init__(self): + self.app = None + self.inspector = None + self.changes_made = [] + self.backup_manager = None + self.backup_file = None + + def print_header(self, title): + """Print formatted header""" + print("\n" + "=" * 70) + print(f" {title}") + print("=" * 70) + + def print_step(self, step_num, description): + """Print step information""" + print(f"\n[Step {step_num}] {description}") + print("-" * 70) + + def initialize(self): + """Initialize Flask app and database""" + try: + from app import create_app, db + from sqlalchemy import inspect, text + + self.app = create_app() + self.db = db + self.text = text + + # Get database path + with self.app.app_context(): + db_uri = self.app.config['SQLALCHEMY_DATABASE_URI'] + if db_uri.startswith('sqlite:///'): + db_path = db_uri.replace('sqlite:///', '') + self.db_path = Path(db_path) + self.backup_manager = DatabaseBackupManager(self.db_path) + else: + raise ValueError("This script only supports SQLite databases") + + self.inspector = inspect(db.engine) + + return True + + except ImportError as e: + print(f"❌ Error importing modules: {e}") + print("Make sure you're running this from the backend directory") + return False + except Exception as e: + print(f"❌ Initialization failed: {e}") + return False + + def create_backup(self): + """Create database backup""" + self.print_step(1, "Creating Database Backup") + + try: + self.backup_file = self.backup_manager.create_backup() + return True + except Exception as e: + print(f"❌ Backup failed: {e}") + return False + + def check_prerequisites(self): + """Check if all required tables exist""" + self.print_step(2, "Checking Prerequisites") + + with self.app.app_context(): + tables = self.inspector.get_table_names() + + required_tables = ['users', 'machines'] + missing_tables = [t for t in required_tables if t not in tables] + + if missing_tables: + raise MigrationError( + f"Required tables missing: {', '.join(missing_tables)}\n" + "Please ensure your database is properly initialized." + ) + + print("✅ All required tables exist") + print(f"✅ Found tables: {', '.join(tables)}") + + def add_assigned_to_column(self): + """Add assigned_to column to users table if not exists""" + self.print_step(3, "Adding assigned_to Column to Users Table") + + with self.app.app_context(): + columns = [col['name'] for col in self.inspector.get_columns('users')] + + if 'assigned_to' in columns: + print("⚠️ assigned_to column already exists - skipping") + return False + + try: + print("Creating assigned_to column...") + + # Add column + self.db.session.execute(self.text(""" + ALTER TABLE users + ADD COLUMN assigned_to INTEGER NULL + """)) + + # Add foreign key constraint + self.db.session.execute(self.text(""" + ALTER TABLE users + ADD CONSTRAINT fk_users_assigned_to + FOREIGN KEY (assigned_to) + REFERENCES users(id) + ON DELETE SET NULL + """)) + + self.db.session.commit() + + print("✅ assigned_to column created successfully") + print("✅ Foreign key constraint added") + + self.changes_made.append('assigned_to_column') + return True + + except Exception as e: + self.db.session.rollback() + raise MigrationError(f"Failed to add assigned_to column: {str(e)}") + + def create_refiller_machines_table(self): + """Create refiller_machines junction table if not exists""" + self.print_step(4, "Creating refiller_machines Junction Table") + + with self.app.app_context(): + tables = self.inspector.get_table_names() + + if 'refiller_machines' in tables: + print("⚠️ refiller_machines table already exists - skipping") + return False + + try: + print("Creating refiller_machines table...") + + self.db.session.execute(self.text(""" + CREATE TABLE refiller_machines ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + refiller_id INTEGER NOT NULL, + machine_id VARCHAR(10) NOT NULL, + assigned_at DATETIME DEFAULT CURRENT_TIMESTAMP, + assigned_by INTEGER NULL, + + CONSTRAINT fk_refiller_machines_refiller + FOREIGN KEY (refiller_id) + REFERENCES users(id) + ON DELETE CASCADE, + + CONSTRAINT fk_refiller_machines_machine + FOREIGN KEY (machine_id) + REFERENCES machines(machine_id) + ON DELETE CASCADE, + + CONSTRAINT fk_refiller_machines_assigner + FOREIGN KEY (assigned_by) + REFERENCES users(id) + ON DELETE SET NULL, + + CONSTRAINT unique_refiller_machine + UNIQUE (refiller_id, machine_id) + ) + """)) + + self.db.session.commit() + + print("✅ refiller_machines table created successfully") + + self.changes_made.append('refiller_machines_table') + return True + + except Exception as e: + self.db.session.rollback() + raise MigrationError(f"Failed to create refiller_machines table: {str(e)}") + + def verify_migration(self): + """Verify all changes were applied correctly""" + self.print_step(5, "Verifying Migration") + + with self.app.app_context(): + # Refresh inspector + from sqlalchemy import inspect + self.inspector = inspect(self.db.engine) + + # Check assigned_to column + user_columns = [col['name'] for col in self.inspector.get_columns('users')] + if 'assigned_to' in user_columns: + print("✅ users.assigned_to column exists") + else: + raise MigrationError("Verification failed: assigned_to column not found") + + # Check refiller_machines table + tables = self.inspector.get_table_names() + if 'refiller_machines' in tables: + print("✅ refiller_machines table exists") + + # Verify columns + rm_columns = [col['name'] for col in self.inspector.get_columns('refiller_machines')] + expected_columns = ['id', 'refiller_id', 'machine_id', 'assigned_at', 'assigned_by'] + + for col in expected_columns: + if col in rm_columns: + print(f" ✅ Column '{col}' exists") + else: + raise MigrationError(f"Verification failed: Column '{col}' not found") + else: + raise MigrationError("Verification failed: refiller_machines table not found") + + def restore_on_failure(self): + """Restore database from backup on migration failure""" + self.print_header("🔄 RESTORING FROM BACKUP") + + try: + if self.backup_file: + self.backup_manager.restore_backup(self.backup_file) + print("✅ Database restored successfully from backup") + print(" Your data is safe!") + else: + print("⚠️ No backup file available for restore") + except Exception as e: + print(f"❌ Restore failed: {e}") + print(" Please manually restore from backup") + + def print_summary(self): + """Print migration summary""" + self.print_header("✅ MIGRATION COMPLETED SUCCESSFULLY") + + if not self.changes_made: + print("\n⚠️ No changes were made - all structures already exist") + else: + print(f"\n✅ Successfully applied {len(self.changes_made)} change(s):") + for i, change in enumerate(self.changes_made, 1): + print(f" {i}. {change}") + + # Show backup information + if self.backup_file: + print(f"\n📦 Backup Information:") + print(f" Location: {self.backup_file}") + print(f" You can restore this backup if needed using:") + print(f" python migrate_with_backup.py restore") + + # Cleanup old backups + print() + self.backup_manager.cleanup_old_backups(keep_count=10) + + print("\n" + "=" * 70) + print("NEXT STEPS") + print("=" * 70) + print("\n1. Update Backend Models (app/models/models.py)") + print("2. Update Backend Services (app/services/services.py)") + print("3. Update Backend Routes (app/routes/routes.py)") + print("4. Update Frontend (user and machine modules)") + print("5. Restart Backend: python app.py") + print("6. Test the implementation") + print("\n" + "=" * 70 + "\n") + + def run_migration(self): + """Run the complete migration process""" + try: + self.print_header("🚀 Option 3 Migration with Backup") + + # Step 1: Create backup + if not self.create_backup(): + print("❌ Cannot proceed without backup") + return False + + # Step 2: Check prerequisites + self.check_prerequisites() + + # Step 3: Add assigned_to column + self.add_assigned_to_column() + + # Step 4: Create refiller_machines table + self.create_refiller_machines_table() + + # Step 5: Verify migration + self.verify_migration() + + # Step 6: Print summary + self.print_summary() + + return True + + except MigrationError as e: + print(f"\n❌ Migration Error: {str(e)}") + self.restore_on_failure() + return False + + except Exception as e: + print(f"\n❌ Unexpected Error: {str(e)}") + import traceback + traceback.print_exc() + self.restore_on_failure() + return False + + +def list_backups(): + """List all available backups""" + print("\n" + "=" * 70) + print(" AVAILABLE BACKUPS") + print("=" * 70) + + # Find database path + try: + from app import create_app + app = create_app() + with app.app_context(): + db_uri = app.config['SQLALCHEMY_DATABASE_URI'] + if db_uri.startswith('sqlite:///'): + db_path = db_uri.replace('sqlite:///', '') + backup_manager = DatabaseBackupManager(Path(db_path)) + + backups = backup_manager.list_backups() + + if not backups: + print("\n📂 No backups found") + return + + print(f"\n📂 Found {len(backups)} backup(s):") + print("=" * 70) + + for i, backup in enumerate(backups, 1): + size = backup.stat().st_size / (1024 * 1024) + mtime = datetime.fromtimestamp(backup.stat().st_mtime) + + print(f"{i:2d}. {backup.name}") + print(f" Date: {mtime.strftime('%Y-%m-%d %H:%M:%S')}") + print(f" Size: {size:.2f} MB") + print() + + print("=" * 70 + "\n") + else: + print("❌ Only SQLite databases are supported") + except Exception as e: + print(f"❌ Error: {e}") + + +def restore_from_backup(): + """Interactive restore from backup""" + print("\n" + "=" * 70) + print(" RESTORE FROM BACKUP") + print("=" * 70) + + try: + from app import create_app + app = create_app() + with app.app_context(): + db_uri = app.config['SQLALCHEMY_DATABASE_URI'] + if db_uri.startswith('sqlite:///'): + db_path = db_uri.replace('sqlite:///', '') + backup_manager = DatabaseBackupManager(Path(db_path)) + + backups = backup_manager.list_backups() + + if not backups: + print("\n❌ No backups found") + return + + # List backups + print(f"\n📂 Available backups:") + print("=" * 70) + + for i, backup in enumerate(backups, 1): + size = backup.stat().st_size / (1024 * 1024) + mtime = datetime.fromtimestamp(backup.stat().st_mtime) + + print(f"{i:2d}. {backup.name}") + print(f" Date: {mtime.strftime('%Y-%m-%d %H:%M:%S')}") + print(f" Size: {size:.2f} MB") + print() + + print("=" * 70) + + # Get user choice + choice = input("\nEnter backup number to restore (or 'q' to quit): ").strip() + + if choice.lower() == 'q': + print("❌ Restore cancelled") + return + + try: + index = int(choice) - 1 + if index < 0 or index >= len(backups): + print("❌ Invalid backup number") + return + + backup_file = backups[index] + + # Confirm + print(f"\n⚠️ WARNING: This will replace your current database!") + print(f" Backup: {backup_file.name}") + confirm = input("\nType 'yes' to confirm restore: ").strip().lower() + + if confirm != 'yes': + print("❌ Restore cancelled") + return + + # Restore + backup_manager.restore_backup(backup_file) + print("\n⚠️ Remember to restart your Flask server!") + + except ValueError: + print("❌ Invalid input") + except Exception as e: + print(f"❌ Restore failed: {e}") + else: + print("❌ Only SQLite databases are supported") + except Exception as e: + print(f"❌ Error: {e}") + + +def main(): + """Main entry point""" + print("\n" + "=" * 70) + print(" VENDING MACHINE MANAGEMENT SYSTEM") + print(" Database Migration with Backup & Restore") + print(" Version: 2.0.0") + print("=" * 70) + + if len(sys.argv) > 1: + command = sys.argv[1].lower() + + if command == 'list': + list_backups() + return + elif command == 'restore': + restore_from_backup() + return + elif command == 'migrate': + pass # Continue to migration + else: + print(f"\n❌ Unknown command: {command}") + print("\nUsage:") + print(" python migrate_with_backup.py # Run migration") + print(" python migrate_with_backup.py migrate # Run migration") + print(" python migrate_with_backup.py list # List backups") + print(" python migrate_with_backup.py restore # Restore from backup") + return + + # Run migration + migration = Option3MigrationWithBackup() + + if not migration.initialize(): + print("\n❌ Failed to initialize. Please check your setup.") + sys.exit(1) + + print("\nThis migration will:") + print(" 1. ✅ Create backup of current database") + print(" 2. ✅ Add users.assigned_to column") + print(" 3. ✅ Create refiller_machines table") + print(" 4. ✅ Verify all changes") + print(" 5. ✅ Auto-restore on failure") + + response = input("\nProceed with migration? (yes/no): ").strip().lower() + + if response != 'yes': + print("\n❌ Migration cancelled") + sys.exit(0) + + success = migration.run_migration() + + if success: + print("\n✅ Migration completed successfully!") + sys.exit(0) + else: + print("\n❌ Migration failed! Database has been restored from backup.") + sys.exit(1) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Machine-Backend/wait_for_db.py b/Machine-Backend/wait_for_db.py index 28618ea..699c2e8 100644 --- a/Machine-Backend/wait_for_db.py +++ b/Machine-Backend/wait_for_db.py @@ -1,38 +1,123 @@ -import time -import sys -import os -from sqlalchemy import create_engine -from sqlalchemy.exc import OperationalError +""" +Database Migration Script for Categories Table +""" -def wait_for_db(max_retries=30, retry_delay=2): - """Wait for database to be ready""" +from app import db, create_app +from sqlalchemy import text + +def create_categories_table(): + """Create categories table""" - mysql_host = os.getenv('MYSQL_HOST', 'db') - mysql_user = os.getenv('MYSQL_USER', 'vendinguser') - mysql_password = os.getenv('MYSQL_PASSWORD', 'vendingpass') - mysql_db = os.getenv('MYSQL_DATABASE', 'vending') + create_table_sql = """ + CREATE TABLE IF NOT EXISTS categories ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + category_id VARCHAR(50) UNIQUE NOT NULL, + name VARCHAR(100) NOT NULL, + image VARCHAR(255), + brand_id VARCHAR(50) NOT NULL, + brand_name VARCHAR(100) NOT NULL, + branch_id VARCHAR(50) NOT NULL, + branch_name VARCHAR(100) NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (brand_id) REFERENCES brands(brand_id), + FOREIGN KEY (branch_id) REFERENCES branches(branch_id) + ); + """ - db_uri = f'mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:3306/{mysql_db}' + create_index_sqls = [ + "CREATE INDEX IF NOT EXISTS idx_category_id ON categories(category_id);", + "CREATE INDEX IF NOT EXISTS idx_category_brand_id ON categories(brand_id);", + "CREATE INDEX IF NOT EXISTS idx_category_branch_id ON categories(branch_id);", + "CREATE INDEX IF NOT EXISTS idx_category_name ON categories(name);" + ] - print(f"⏳ Waiting for MySQL at {mysql_host}:3306...") + try: + app = create_app() + with app.app_context(): + with db.engine.connect() as connection: + # Create the categories table + connection.execute(text(create_table_sql)) + + # Create each index separately (SQLite limitation) + for sql in create_index_sqls: + connection.execute(text(sql)) + + connection.commit() + + print("✓ Categories table created successfully!") + print("✓ Indexes created successfully!") + + # Optional: add sample categories + add_sample = input("\nAdd sample categories? (y/n): ") + if add_sample.lower() == 'y': + add_sample_categories() + + except Exception as e: + print(f"✗ Error creating table: {e}") + import traceback + traceback.print_exc() + + +def add_sample_categories(): + """Add sample category data""" + from app.models.models import Category, Brand, Branch - for attempt in range(1, max_retries + 1): - try: - engine = create_engine(db_uri) - connection = engine.connect() - connection.close() - print("✓ MySQL is ready!") - return True - except OperationalError as e: - if attempt < max_retries: - print(f"Waiting for MySQL... (attempt {attempt}/{max_retries})") - time.sleep(retry_delay) - else: - print(f"❌ Failed to connect to MySQL after {max_retries} attempts") - print(f"Error: {e}") - sys.exit(1) + brands = Brand.query.all() + branches = Branch.query.all() - return False + if not brands or not branches: + print("✗ Please create brands and branches first!") + return + + sample_categories = [ + {'name': 'Beverages', 'brand_id': brands[0].brand_id if len(brands) > 0 else None}, + {'name': 'Snacks', 'brand_id': brands[1].brand_id if len(brands) > 1 else brands[0].brand_id}, + {'name': 'Chocolates', 'brand_id': brands[1].brand_id if len(brands) > 1 else brands[0].brand_id} + ] + + try: + for cat_data in sample_categories: + if not cat_data['brand_id']: + continue + + brand = Brand.query.filter_by(brand_id=cat_data['brand_id']).first() + if not brand: + continue + + branch = Branch.query.filter_by(branch_id=brand.branch_id).first() + if not branch: + continue + + category_id = Category.generate_category_id(cat_data['name']) + + category = Category( + category_id=category_id, + name=cat_data['name'], + brand_id=brand.brand_id, + brand_name=brand.name, + branch_id=branch.branch_id, + branch_name=branch.name, + image=None + ) + + db.session.add(category) + + db.session.commit() + print(f"✓ Added sample categories successfully!") + + # Display all created categories + categories = Category.query.all() + print("\nCreated categories:") + for cat in categories: + print(f" - {cat.category_id}: {cat.name} @ {cat.brand_name} ({cat.branch_name})") + + except Exception as e: + db.session.rollback() + print(f"✗ Error adding sample data: {e}") + import traceback + traceback.print_exc() + if __name__ == '__main__': - wait_for_db() \ No newline at end of file + create_categories_table() diff --git a/Project/microservice.py b/Project/microservice.py index 31be3a3..074c30d 100644 --- a/Project/microservice.py +++ b/Project/microservice.py @@ -15,8 +15,8 @@ CORS(app, ) # Configuration - point to your main backend -# MAIN_BACKEND_URL = "http://127.0.0.1:5000" -MAIN_BACKEND_URL = "https://iotbackend.rootxwire.com" # Change this to your main backend URL +MAIN_BACKEND_URL = "http://127.0.0.1:5000" +# MAIN_BACKEND_URL = "https://iotbackend.rootxwire.com" # Change this to your main backend URL # Add OPTIONS handler for preflight requests @app.before_request diff --git a/fuse-starter-v20.0.0/src/app/core/services/role-state.service.ts b/fuse-starter-v20.0.0/src/app/core/services/role-state.service.ts new file mode 100644 index 0000000..396aabb --- /dev/null +++ b/fuse-starter-v20.0.0/src/app/core/services/role-state.service.ts @@ -0,0 +1,78 @@ +// app/core/services/role-state.service.ts +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; + +export interface RoleUpdate { + roleId: number; + roleName: string; + permissions: string[]; + timestamp: number; +} + +@Injectable({ + providedIn: 'root' +}) +export class RoleStateService { + // Track when roles are updated + private _roleUpdated = new BehaviorSubject(null); + public roleUpdated$ = this._roleUpdated.asObservable(); + + // Track when a role is deleted + private _roleDeleted = new BehaviorSubject(null); + public roleDeleted$ = this._roleDeleted.asObservable(); + + // Track when a role is created + private _roleCreated = new BehaviorSubject(null); + public roleCreated$ = this._roleCreated.asObservable(); + + constructor() { + console.log('✓ RoleStateService initialized'); + } + + /** + * Notify that a role's permissions have been updated + */ + notifyRoleUpdate(roleId: number, roleName: string, permissions: string[]): void { + const update: RoleUpdate = { + roleId, + roleName, + permissions, + timestamp: Date.now() + }; + + console.log('📢 Role updated:', update); + this._roleUpdated.next(update); + } + + /** + * Notify that a role has been deleted + */ + notifyRoleDeleted(roleId: number): void { + console.log('📢 Role deleted:', roleId); + this._roleDeleted.next(roleId); + } + + /** + * Notify that a role has been created + */ + notifyRoleCreated(roleId: number, roleName: string, permissions: string[]): void { + const update: RoleUpdate = { + roleId, + roleName, + permissions, + timestamp: Date.now() + }; + + console.log('📢 Role created:', update); + this._roleCreated.next(update); + } + + /** + * Clear all notifications + */ + clear(): void { + this._roleUpdated.next(null); + this._roleDeleted.next(null); + this._roleCreated.next(null); + } +} \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/mock-api/common/navigation/data.ts b/fuse-starter-v20.0.0/src/app/mock-api/common/navigation/data.ts index 31a4ccb..7e24dc7 100644 --- a/fuse-starter-v20.0.0/src/app/mock-api/common/navigation/data.ts +++ b/fuse-starter-v20.0.0/src/app/mock-api/common/navigation/data.ts @@ -120,96 +120,8 @@ export const defaultNavigation: FuseNavigationItem[] = [ icon: 'heroicons_outline:user-group', link: '/role-management', }, - { - id: 'dashboards.warehouse', - title: 'Warehouse', - type: 'collapsable', - icon: 'heroicons_outline:truck', - link: '/dashboards/warehouse', - children: [ - { - id: 'dashboards.Warehouse.warehouse-list', - title: 'Warehouse List', - type: 'basic', - link: '/warehouse/warehouse-list', - }, - { - id: 'dashboards.Warehouse.vendors', - title: 'Vendors', - type: 'basic', - link: '/warehouse/vendors', - }, - { - id: 'dashboards.Warehouse.current-stock', - title: 'Current Stock', - type: 'basic', - link: '/warehouse/current-stock', - }, - { - id: 'dashboards.Warehouse.stock-in-transit', - title: 'Stock In Transit', - type: 'basic', - link: '/warehouse/stock-in-transit', - }, - { - id: 'dashboards.Warehouse.returned-stock', - title: 'Returned Stock', - type: 'basic', - link: '/warehouse/returned-stock', - }, - { - id: 'dashboards.Warehouse.scrapped-stock', - title: 'Scrapped Stock', - type: 'basic', - link: '/warehouse/scrapped-stock', - }, - ], - }, - { - id: 'dashboards.w-transactions', - title: 'W. Transactions', - type: 'collapsable', - icon: 'heroicons_outline:newspaper', - link: '/dashboards/w-transactions', - children: [ - { - id: 'dashboards.w-transactions.purchase', - title: 'Purchase', - type: 'basic', - link: '/w-transactions/purchase', - }, - { - id: 'dashboards.w-transactions.transfer-request', - title: 'Transfer Request', - type: 'basic', - link: '/w-transactions/transfer-request', - }, - { - id: 'dashboards.w-transactions.refill-request', - title: 'Refill Request', - type: 'basic', - link: '/w-transactions/refill-request', - }, - { - id: 'dashboards.w-transactions.return-request', - title: 'Return Request', - type: 'basic', - link: '/w-transactions/return-request', - }, - { - id: 'dashboards.w-transactions.maintenance-request', - title: 'Maintenance Request', - type: 'basic', - link: '/w-transactions/maintenance-request', - }, - { - id: 'dashboards.w-transactions.maintenance-history', - title: 'Maintenance History', - type: 'basic', - link: '/w-transactions/maintenance-history', - }, - ], - }, + // Warehouse - Hidden + // W. Transactions - Hidden { id: 'dashboards.advertisements', title: 'Advertisements', @@ -249,33 +161,7 @@ export const defaultNavigation: FuseNavigationItem[] = [ }, ], }, - { - id: 'dashboards.user-management', - title: 'User Management', - type: 'collapsable', - icon: 'heroicons_outline:user-circle', - link: '/dashboards/user-management', - children: [ - { - id: 'dashboards.user-management.client.admin-list', - title: 'Client Admin List', - type: 'basic', - link: '/user-management/client-admin-list', - }, - { - id: 'dashboards.user-management.client-user-list', - title: 'Client User List', - type: 'basic', - link: '/user-management/client-user-list', - }, - { - id: 'dashboards.user-management.validation-group-list', - title: 'Validation Group List', - type: 'basic', - link: '/user-management/validation-group-list', - }, - ], - }, + // User Management - Hidden { id: 'dashboards.api-integration', title: 'API Integration', @@ -308,18 +194,18 @@ export const defaultNavigation: FuseNavigationItem[] = [ type: 'basic', link: '/company/company-admin-list', }, - { - id: 'dashboards.company.company-user-list', - title: 'Company User List', - type: 'basic', - link: '/company/company-user-list', - }, - { - id: 'dashboards.company.company-user-role-list', - title: 'Company User Role List', - type: 'basic', - link: 'company/company-user-role-list', - }, + // { + // id: 'dashboards.company.company-user-list', + // title: 'Company User List', + // type: 'basic', + // link: '/company/company-user-list', + // }, + // { + // id: 'dashboards.company.company-user-role-list', + // title: 'Company User Role List', + // type: 'basic', + // link: 'company/company-user-role-list', + // }, ], }, { @@ -349,27 +235,7 @@ export const defaultNavigation: FuseNavigationItem[] = [ }, ], }, - { - id: 'dashboards.offers-coupons', - title: 'Offers & Coupons', - type: 'collapsable', - icon: 'heroicons_outline:banknotes', - link: '/dashboards/offers-coupons', - children: [ - { - id: 'dashboards.offers-coupons.offers', - title: 'Offers', - type: 'basic', - link: '/offers-coupons/offers', - }, - { - id: 'dashboards.offers-coupons.old-offers', - title: 'Old Offers', - type: 'basic', - link: '/offers-coupons/old-offers', - }, - ], - }, + // Offers & Coupons - Hidden { id: 'dashboards.help-support', title: 'Help & Support', @@ -377,14 +243,7 @@ export const defaultNavigation: FuseNavigationItem[] = [ icon: 'heroicons_outline:information-circle', link: '/help-support', }, - { - id: 'dashboards.support-history', - title: 'Support History', - type: 'basic', - icon: 'heroicons_outline:clock', - link: '/support-history', - }, - + // Support History - Hidden ], }, ]; @@ -395,7 +254,7 @@ export const compactNavigation: FuseNavigationItem[] = [ tooltip: 'Dashboards', type: 'aside', icon: 'heroicons_outline:home', - children: [], // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation + children: [], }, ]; export const futuristicNavigation: FuseNavigationItem[] = [ @@ -403,7 +262,7 @@ export const futuristicNavigation: FuseNavigationItem[] = [ id: 'dashboards', title: 'DASHBOARDS', type: 'group', - children: [], // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation + children: [], }, ]; export const horizontalNavigation: FuseNavigationItem[] = [ @@ -412,6 +271,6 @@ export const horizontalNavigation: FuseNavigationItem[] = [ title: 'Dashboards', type: 'group', icon: 'heroicons_outline:home', - children: [], // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation + children: [], }, ]; \ No newline at end of file diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.html b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.html index 8bd6e11..4172517 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.html +++ b/fuse-starter-v20.0.0/src/app/modules/admin/dashboard/company/branch-list/branch-list.component.html @@ -1 +1,326 @@ -