web controllers added
|
|
@ -0,0 +1,164 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
.pdm.toml
|
||||
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
.idea/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
from schemas.account.account import (
|
||||
AccountBooks,
|
||||
AccountCodes,
|
||||
AccountCodeParser,
|
||||
AccountMaster,
|
||||
AccountDetail,
|
||||
AccountRecordExchanges,
|
||||
AccountRecords,
|
||||
)
|
||||
from schemas.account.iban import (
|
||||
BuildIbans,
|
||||
BuildIbanDescription,
|
||||
)
|
||||
from schemas.address.address import (
|
||||
RelationshipEmployee2PostCode,
|
||||
AddressPostcode,
|
||||
Addresses,
|
||||
AddressGeographicLocations,
|
||||
AddressCountry,
|
||||
AddressState,
|
||||
AddressCity,
|
||||
AddressDistrict,
|
||||
AddressLocality,
|
||||
AddressNeighborhood,
|
||||
AddressStreet,
|
||||
)
|
||||
from schemas.building.build import (
|
||||
BuildTypes,
|
||||
Part2Employee,
|
||||
RelationshipEmployee2Build,
|
||||
Build,
|
||||
BuildParts,
|
||||
BuildLivingSpace,
|
||||
BuildManagement,
|
||||
BuildArea,
|
||||
BuildSites,
|
||||
BuildCompaniesProviding,
|
||||
BuildPersonProviding,
|
||||
)
|
||||
from schemas.building.decision_book import (
|
||||
BuildDecisionBook,
|
||||
BuildDecisionBookInvitations,
|
||||
BuildDecisionBookPerson,
|
||||
BuildDecisionBookPersonOccupants,
|
||||
BuildDecisionBookItems,
|
||||
BuildDecisionBookItemsUnapproved,
|
||||
BuildDecisionBookPayments,
|
||||
BuildDecisionBookLegal,
|
||||
BuildDecisionBookProjects,
|
||||
BuildDecisionBookProjectPerson,
|
||||
BuildDecisionBookProjectItems,
|
||||
)
|
||||
from schemas.building.budget import (
|
||||
DecisionBookBudgetBooks,
|
||||
DecisionBookBudgetCodes,
|
||||
DecisionBookBudgetMaster,
|
||||
DecisionBookBudgets,
|
||||
)
|
||||
from schemas.company.company import (
|
||||
Companies,
|
||||
RelationshipDutyCompany,
|
||||
)
|
||||
from schemas.company.employee import (
|
||||
Employees,
|
||||
EmployeesSalaries,
|
||||
EmployeeHistory,
|
||||
Staff,
|
||||
)
|
||||
from schemas.company.department import (
|
||||
Duty,
|
||||
Duties,
|
||||
Departments,
|
||||
)
|
||||
from schemas.event.event import (
|
||||
Modules,
|
||||
Services,
|
||||
Service2Events,
|
||||
Service2Application,
|
||||
Events,
|
||||
Event2Occupant,
|
||||
Event2Employee,
|
||||
Event2OccupantExtra,
|
||||
Event2EmployeeExtra,
|
||||
Applications,
|
||||
Application2Employee,
|
||||
Application2Occupant,
|
||||
Application2EmployeeExtra,
|
||||
Application2OccupantExtra,
|
||||
)
|
||||
from schemas.identity.identity import (
|
||||
UsersTokens,
|
||||
OccupantTypes,
|
||||
People,
|
||||
Users,
|
||||
Credentials,
|
||||
RelationshipDutyPeople,
|
||||
Contracts,
|
||||
)
|
||||
from schemas.address.address import (
|
||||
Addresses,
|
||||
AddressCity,
|
||||
AddressStreet,
|
||||
AddressLocality,
|
||||
AddressDistrict,
|
||||
AddressNeighborhood,
|
||||
AddressState,
|
||||
AddressCountry,
|
||||
AddressPostcode,
|
||||
AddressGeographicLocations,
|
||||
RelationshipEmployee2PostCode,
|
||||
)
|
||||
from schemas.others.enums import (
|
||||
ApiEnumDropdown,
|
||||
)
|
||||
from schemas.rules.rules import (
|
||||
EndpointRestriction,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AccountBooks",
|
||||
"AccountCodes",
|
||||
"AccountCodeParser",
|
||||
"AccountMaster",
|
||||
"AccountDetail",
|
||||
"AccountRecordExchanges",
|
||||
"AccountRecords",
|
||||
"BuildIbans",
|
||||
"BuildIbanDescription",
|
||||
"RelationshipEmployee2PostCode",
|
||||
"AddressPostcode",
|
||||
"Addresses",
|
||||
"AddressGeographicLocations",
|
||||
"AddressCountry",
|
||||
"AddressState",
|
||||
"AddressCity",
|
||||
"AddressDistrict",
|
||||
"AddressLocality",
|
||||
"AddressNeighborhood",
|
||||
"AddressStreet",
|
||||
"BuildTypes",
|
||||
"Part2Employee",
|
||||
"RelationshipEmployee2Build",
|
||||
"Build",
|
||||
"BuildParts",
|
||||
"BuildLivingSpace",
|
||||
"BuildManagement",
|
||||
"BuildArea",
|
||||
"BuildSites",
|
||||
"BuildCompaniesProviding",
|
||||
"BuildPersonProviding",
|
||||
"BuildDecisionBook",
|
||||
"BuildDecisionBookInvitations",
|
||||
"BuildDecisionBookPerson",
|
||||
"BuildDecisionBookPersonOccupants",
|
||||
"BuildDecisionBookItems",
|
||||
"BuildDecisionBookItemsUnapproved",
|
||||
"BuildDecisionBookPayments",
|
||||
"BuildDecisionBookLegal",
|
||||
"BuildDecisionBookProjects",
|
||||
"BuildDecisionBookProjectPerson",
|
||||
"BuildDecisionBookPersonOccupants",
|
||||
"BuildDecisionBookProjectItems",
|
||||
"DecisionBookBudgetBooks",
|
||||
"DecisionBookBudgetCodes",
|
||||
"DecisionBookBudgetMaster",
|
||||
"DecisionBookBudgets",
|
||||
"Companies",
|
||||
"RelationshipDutyCompany",
|
||||
"Employees",
|
||||
"EmployeesSalaries",
|
||||
"EmployeeHistory",
|
||||
"Staff",
|
||||
"Duty",
|
||||
"Duties",
|
||||
"Departments",
|
||||
"Modules",
|
||||
"Services",
|
||||
"Service2Events",
|
||||
"Events",
|
||||
"Event2Occupant",
|
||||
"Event2Employee",
|
||||
"Event2OccupantExtra",
|
||||
"Event2EmployeeExtra",
|
||||
"Applications",
|
||||
"Application2Employee",
|
||||
"Application2Occupant",
|
||||
"Addresses",
|
||||
"AddressCity",
|
||||
"AddressStreet",
|
||||
"AddressLocality",
|
||||
"AddressDistrict",
|
||||
"AddressNeighborhood",
|
||||
"AddressState",
|
||||
"AddressCountry",
|
||||
"AddressPostcode",
|
||||
"AddressGeographicLocations",
|
||||
"UsersTokens",
|
||||
"OccupantTypes",
|
||||
"People",
|
||||
"Users",
|
||||
"Credentials",
|
||||
"RelationshipDutyPeople",
|
||||
"RelationshipEmployee2PostCode",
|
||||
"Contracts",
|
||||
"ApiEnumDropdown",
|
||||
"EndpointRestriction",
|
||||
"RelationshipEmployee2Build",
|
||||
# ------------------------------------------------
|
||||
]
|
||||
|
|
@ -0,0 +1,575 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class AccountBooks(CrudCollection):
|
||||
|
||||
__tablename__ = "account_books"
|
||||
__exclude__fields__ = []
|
||||
|
||||
country: Mapped[str] = mapped_column(String, nullable=False)
|
||||
branch_type: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=False)
|
||||
company_uu_id: Mapped[str] = mapped_column(String, nullable=False)
|
||||
branch_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
branch_uu_id: Mapped[str] = mapped_column(String, comment="Branch UU ID")
|
||||
|
||||
__table_args__ = (
|
||||
Index("account_companies_book_ndx_00", company_id, "expiry_starts"),
|
||||
{"comment": "Account Book Information"},
|
||||
)
|
||||
|
||||
|
||||
class AccountCodes(CrudCollection):
|
||||
|
||||
__tablename__ = "account_codes"
|
||||
__exclude__fields__ = []
|
||||
|
||||
account_code: Mapped[str] = mapped_column(
|
||||
String(48), nullable=False, comment="Account Code"
|
||||
)
|
||||
comment_line: Mapped[str] = mapped_column(
|
||||
String(128), nullable=False, comment="Comment Line"
|
||||
)
|
||||
|
||||
is_receive_or_debit: Mapped[bool] = mapped_column(Boolean)
|
||||
product_id: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
nvi_id: Mapped[str] = mapped_column(String(48), server_default="")
|
||||
status_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
account_code_seperator: Mapped[str] = mapped_column(String(1), server_default=".")
|
||||
|
||||
system_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
locked: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Company UU ID"
|
||||
)
|
||||
customer_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
customer_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Customer UU ID"
|
||||
)
|
||||
person_id: Mapped[int] = mapped_column(ForeignKey("people.id"))
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Person UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = ({"comment": "Account Code Information"},)
|
||||
|
||||
|
||||
class AccountCodeParser(CrudCollection):
|
||||
|
||||
__tablename__ = "account_code_parser"
|
||||
__exclude__fields__ = []
|
||||
|
||||
account_code_1: Mapped[str] = mapped_column(String, nullable=False, comment="Order")
|
||||
account_code_2: Mapped[str] = mapped_column(String, nullable=False, comment="Order")
|
||||
account_code_3: Mapped[str] = mapped_column(String, nullable=False, comment="Order")
|
||||
account_code_4: Mapped[str] = mapped_column(String, server_default="")
|
||||
account_code_5: Mapped[str] = mapped_column(String, server_default="")
|
||||
account_code_6: Mapped[str] = mapped_column(String, server_default="")
|
||||
|
||||
account_code_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_codes.id"), nullable=False
|
||||
)
|
||||
account_code_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Account Code UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_account_code_parser_ndx_00", account_code_id),
|
||||
{"comment": "Account Code Parser Information"},
|
||||
)
|
||||
|
||||
@property
|
||||
def get_account_code(self):
|
||||
return f"{self.account_codes.account_code_seperator}".join(
|
||||
[
|
||||
getattr(self, f"account_code_{i}")
|
||||
for i in range(1, 7)
|
||||
if getattr(self, f"account_code_{i}")
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AccountMaster(CrudCollection):
|
||||
"""
|
||||
AccountCodes class based on declarative_base and CrudCollection via session
|
||||
"""
|
||||
|
||||
__tablename__ = "account_master"
|
||||
__exclude__fields__ = []
|
||||
|
||||
doc_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Document Date"
|
||||
)
|
||||
plug_type: Mapped[str] = mapped_column(String, nullable=False, comment="Plug Type")
|
||||
plug_number: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, comment="Plug Number"
|
||||
)
|
||||
|
||||
special_code: Mapped[str] = mapped_column(String(12), server_default="")
|
||||
authorization_code: Mapped[str] = mapped_column(String(12), server_default="")
|
||||
|
||||
doc_code: Mapped[str] = mapped_column(String(12), server_default="")
|
||||
doc_type: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
comment_line1: Mapped[str] = mapped_column(String, server_default="")
|
||||
comment_line2: Mapped[str] = mapped_column(String, server_default="")
|
||||
comment_line3: Mapped[str] = mapped_column(String, server_default="")
|
||||
comment_line4: Mapped[str] = mapped_column(String, server_default="")
|
||||
comment_line5: Mapped[str] = mapped_column(String, server_default="")
|
||||
comment_line6: Mapped[str] = mapped_column(String, server_default="")
|
||||
project_code: Mapped[str] = mapped_column(String(12), server_default="")
|
||||
module_no: Mapped[str] = mapped_column(String, server_default="")
|
||||
journal_no: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
|
||||
status_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
canceled: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
print_count: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
total_active: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_passive: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_active_1: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_passive_1: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_active_2: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_passive_2: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_active_3: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_passive_3: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_active_4: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
total_passive_4: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
cross_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
data_center_id: Mapped[str] = mapped_column(String, server_default="")
|
||||
data_center_rec_num: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
|
||||
account_header_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_books.id"), nullable=False
|
||||
)
|
||||
account_header_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Account Header UU ID"
|
||||
)
|
||||
project_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id")
|
||||
)
|
||||
project_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, comment="Project Item UU ID"
|
||||
)
|
||||
department_id: Mapped[int] = mapped_column(ForeignKey("departments.id"))
|
||||
department_uu_id: Mapped[str] = mapped_column(String, comment="Department UU ID")
|
||||
|
||||
__table_args__ = (
|
||||
Index("_account_master_ndx_00", doc_date, account_header_id),
|
||||
{"comment": "Account Master Information"},
|
||||
)
|
||||
|
||||
|
||||
class AccountDetail(CrudCollection):
|
||||
"""
|
||||
AccountCodes class based on declarative_base and CrudCollection via session
|
||||
"""
|
||||
|
||||
__tablename__ = "account_detail"
|
||||
__exclude__fields__ = []
|
||||
__enum_list__ = [("plug_type", "AccountingReceiptTypes", "M")]
|
||||
|
||||
doc_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Document Date"
|
||||
)
|
||||
line_no: Mapped[int] = mapped_column(
|
||||
SmallInteger, nullable=False, comment="Line Number"
|
||||
)
|
||||
receive_debit: Mapped[str] = mapped_column(
|
||||
String(1), nullable=False, comment="Receive Debit"
|
||||
)
|
||||
debit: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Debit"
|
||||
)
|
||||
|
||||
department: Mapped[str] = mapped_column(String(24), server_default="")
|
||||
special_code: Mapped[str] = mapped_column(String(12), server_default="")
|
||||
account_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
account_fiche_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
center_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
general_code: Mapped[str] = mapped_column(String(32), server_default="")
|
||||
credit: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
currency_type: Mapped[str] = mapped_column(String(4), server_default="TL")
|
||||
exchange_rate: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
debit_cur: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
credit_cur: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
discount_cur: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
amount: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
cross_account_code: Mapped[float] = mapped_column(String(32), server_default="")
|
||||
inf_index: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
not_inflated: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
not_calculated: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
comment_line1: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
comment_line2: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
comment_line3: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
comment_line4: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
comment_line5: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
comment_line6: Mapped[str] = mapped_column(String(64), server_default="")
|
||||
owner_acc_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
from_where: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
orj_eid: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
canceled: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
cross_ref: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
data_center_id: Mapped[str] = mapped_column(String, server_default="")
|
||||
data_center_rec_num: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
status_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
plug_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
plug_type_uu_id = mapped_column(String, nullable=False, comment="Plug Type UU ID")
|
||||
account_header_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_books.id"), nullable=False
|
||||
)
|
||||
account_header_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Account Header UU ID"
|
||||
)
|
||||
account_code_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_codes.id"), nullable=False
|
||||
)
|
||||
account_code_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Account Code UU ID"
|
||||
)
|
||||
account_master_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_master.id"), nullable=False
|
||||
)
|
||||
account_master_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Account Master UU ID"
|
||||
)
|
||||
project_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id")
|
||||
)
|
||||
project_uu_id: Mapped[str] = mapped_column(String, comment="Project UU ID")
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_account_detail_ndx_00",
|
||||
account_master_id,
|
||||
doc_date,
|
||||
line_no,
|
||||
account_header_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Account Detail Information"},
|
||||
)
|
||||
|
||||
|
||||
class AccountRecordExchanges(CrudCollection):
|
||||
|
||||
__tablename__ = "account_record_exchanges"
|
||||
__exclude__fields__ = []
|
||||
|
||||
are_currency: Mapped[str] = mapped_column(
|
||||
String(5), nullable=False, comment="Unit of Currency"
|
||||
)
|
||||
are_exchange_rate: Mapped[float] = mapped_column(
|
||||
Numeric(18, 6), nullable=False, server_default="1"
|
||||
)
|
||||
usd_exchange_rate_value: Mapped[float] = mapped_column(
|
||||
Numeric(18, 6),
|
||||
nullable=True,
|
||||
server_default="0",
|
||||
comment="It will be written by multiplying the usd exchange rate with the current value result.",
|
||||
)
|
||||
eur_exchange_rate_value: Mapped[float] = mapped_column(
|
||||
Numeric(18, 6),
|
||||
nullable=True,
|
||||
server_default="0",
|
||||
comment="It will be written by multiplying the eur exchange rate with the current value result.",
|
||||
)
|
||||
gbp_exchange_rate_value: Mapped[float] = mapped_column(
|
||||
Numeric(18, 6),
|
||||
nullable=True,
|
||||
server_default="0",
|
||||
comment="It will be written by multiplying the gpd exchange rate with the current value result.",
|
||||
)
|
||||
cny_exchange_rate_value: Mapped[float] = mapped_column(
|
||||
Numeric(18, 6),
|
||||
nullable=True,
|
||||
server_default="0",
|
||||
comment="It will be written by multiplying the cny exchange rate with the current value result.",
|
||||
)
|
||||
|
||||
account_records_id: Mapped[int] = mapped_column(ForeignKey("account_records.id"))
|
||||
account_records_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Account Record UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_account_record_exchanges_ndx_00", account_records_id),
|
||||
{"comment": "Account Record Exchanges Information"},
|
||||
)
|
||||
|
||||
|
||||
class AccountRecords(CrudCollection):
|
||||
"""
|
||||
build_decision_book_id = kaydın sorumlu olduğu karar defteri
|
||||
send_company_id = kaydı gönderen firma, send_person_id = gönderen kişi
|
||||
customer_id = sorumlu kullanıcı bilgisi, company_id = sorumlu firma
|
||||
"""
|
||||
|
||||
__tablename__ = "account_records"
|
||||
__exclude__fields__ = []
|
||||
__enum_list__ = [
|
||||
("receive_debit", "DebitTypes", "D"),
|
||||
("budget_type", "BudgetType", "B"),
|
||||
]
|
||||
|
||||
iban: Mapped[str] = mapped_column(
|
||||
String(64), nullable=False, comment="IBAN Number of Bank"
|
||||
)
|
||||
bank_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Bank Transaction Date"
|
||||
)
|
||||
|
||||
currency_value: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Currency Value"
|
||||
)
|
||||
bank_balance: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Bank Balance"
|
||||
)
|
||||
currency: Mapped[str] = mapped_column(
|
||||
String(5), nullable=False, comment="Unit of Currency"
|
||||
)
|
||||
additional_balance: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Additional Balance"
|
||||
)
|
||||
channel_branch: Mapped[str] = mapped_column(
|
||||
String(120), nullable=False, comment="Branch Bank"
|
||||
)
|
||||
process_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Process Type Name"
|
||||
)
|
||||
process_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Process Type"
|
||||
)
|
||||
process_comment: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Transaction Record Comment"
|
||||
)
|
||||
process_garbage: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Transaction Record Garbage"
|
||||
)
|
||||
bank_reference_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Reference Code"
|
||||
)
|
||||
|
||||
add_comment_note: Mapped[str] = mapped_column(String, server_default="")
|
||||
is_receipt_mail_send: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
found_from = mapped_column(String, server_default="")
|
||||
similarity: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
remainder_balance: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
|
||||
bank_date_y: Mapped[int] = mapped_column(Integer)
|
||||
bank_date_m: Mapped[int] = mapped_column(SmallInteger)
|
||||
bank_date_w: Mapped[int] = mapped_column(SmallInteger)
|
||||
bank_date_d: Mapped[int] = mapped_column(SmallInteger)
|
||||
|
||||
approving_accounting_record: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0"
|
||||
)
|
||||
accounting_receipt_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00"
|
||||
)
|
||||
accounting_receipt_number: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
status_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
approved_record: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
import_file_name: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="XLS Key"
|
||||
)
|
||||
|
||||
receive_debit: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
receive_debit_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Debit UU ID"
|
||||
)
|
||||
budget_type: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
budget_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Budget Type UU ID"
|
||||
)
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UU ID"
|
||||
)
|
||||
send_company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
send_company_uu_id = mapped_column(
|
||||
String, nullable=True, comment="Send Company UU ID"
|
||||
)
|
||||
|
||||
send_person_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
send_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Send Person UU ID"
|
||||
)
|
||||
approving_accounting_person: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=True
|
||||
)
|
||||
approving_accounting_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Approving Accounting Person UU ID"
|
||||
)
|
||||
|
||||
living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=True
|
||||
)
|
||||
living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Living Space UU ID"
|
||||
)
|
||||
customer_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
customer_uu_id = mapped_column(String, nullable=True, comment="Customer UU ID")
|
||||
|
||||
build_id: Mapped[int] = mapped_column(ForeignKey("build.id"), nullable=True)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build UU ID"
|
||||
)
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=True
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Parts UU ID"
|
||||
)
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=True
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Decision Book UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_budget_records_ndx_00", is_receipt_mail_send, bank_date),
|
||||
Index(
|
||||
"_budget_records_ndx_01",
|
||||
iban,
|
||||
bank_date,
|
||||
bank_reference_code,
|
||||
bank_balance,
|
||||
unique=True,
|
||||
),
|
||||
Index("_budget_records_ndx_02", status_id, bank_date),
|
||||
{
|
||||
"comment": "Bank Records that are related to building and financial transactions"
|
||||
},
|
||||
)
|
||||
|
||||
# def payment_budget_record_close(self):
|
||||
# from database_sql_models import (
|
||||
# DecisionBookProjectPaymentsMaster,
|
||||
# ApiEnumDropdown,
|
||||
# BuildDecisionBook,
|
||||
# BuildDecisionBookPaymentsMaster,
|
||||
# )
|
||||
#
|
||||
# budget_record = self
|
||||
# if self.receive_debit == ApiEnumDropdown.uuid_of_enum(
|
||||
# enum_class="DebitTypes", key="R"
|
||||
# ):
|
||||
# print(
|
||||
# "This record is not debit. Debit:",
|
||||
# self.receive_debit,
|
||||
# "DebitTypes.R.name",
|
||||
# # str(DebitTypes.R.name),
|
||||
# )
|
||||
# return
|
||||
# if abs(budget_record.currency_value + budget_record.remainder_balance) > 0:
|
||||
# payment_dict = {
|
||||
# "budget_records_id": self.id,
|
||||
# "build_decision_book_id": budget_record.build_decision_book_id,
|
||||
# "build_parts_id": budget_record.build_parts_id,
|
||||
# "start_date": budget_record.bank_date,
|
||||
# "paid_value": budget_record.currency_value
|
||||
# - budget_record.remainder_balance,
|
||||
# "is_all": False,
|
||||
# }
|
||||
# (paid_value, start_paid_value, balance) = (
|
||||
# float(budget_record.currency_value - budget_record.remainder_balance),
|
||||
# float(budget_record.currency_value - budget_record.remainder_balance),
|
||||
# float(budget_record.remainder_balance),
|
||||
# )
|
||||
# print(
|
||||
# "self.id",
|
||||
# self.id,
|
||||
# "paid_value",
|
||||
# paid_value,
|
||||
# "start_paid_value",
|
||||
# start_paid_value,
|
||||
# "balance",
|
||||
# balance,
|
||||
# self.receive_debit,
|
||||
# )
|
||||
#
|
||||
# if not BuildDecisionBook.find_one(
|
||||
# id=payment_dict["build_decision_book_id"]
|
||||
# ):
|
||||
# return paid_value
|
||||
#
|
||||
# if budget_record.replication_id == 55:
|
||||
# if paid_value > 0:
|
||||
# payment_dict["dues_type"] = ApiEnumDropdown.uuid_of_enum(
|
||||
# enum_class="BuildDuesTypes", key="L"
|
||||
# )
|
||||
# paid_value = (
|
||||
# DecisionBookProjectPaymentsMaster.pay_law_and_ren_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# )
|
||||
# print("dues_type", payment_dict["dues_type"], paid_value)
|
||||
# if paid_value > 0:
|
||||
# payment_dict.pop("dues_type", None)
|
||||
# paid_value = BuildDecisionBookPaymentsMaster.pay_dues_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# print("dues_type", None, paid_value)
|
||||
# if paid_value > 0:
|
||||
# payment_dict["dues_type"] = ApiEnumDropdown.uuid_of_enum(
|
||||
# enum_class="BuildDuesTypes", key="R"
|
||||
# )
|
||||
# paid_value = (
|
||||
# DecisionBookProjectPaymentsMaster.pay_law_and_ren_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# )
|
||||
# print("dues_type", payment_dict["dues_type"], paid_value)
|
||||
# payment_dict["is_all"] = True
|
||||
# if paid_value > 0:
|
||||
# payment_dict["dues_type"] = ApiEnumDropdown.uuid_of_enum(
|
||||
# enum_class="BuildDuesTypes", key="L"
|
||||
# )
|
||||
# paid_value = (
|
||||
# DecisionBookProjectPaymentsMaster.pay_law_and_ren_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# )
|
||||
# print("is all dues_type", payment_dict["dues_type"], paid_value)
|
||||
# if paid_value > 0:
|
||||
# payment_dict.pop("dues_type", None)
|
||||
# paid_value = BuildDecisionBookPaymentsMaster.pay_dues_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# print("is all dues_type", None, paid_value)
|
||||
# if paid_value > 0:
|
||||
# payment_dict["dues_type"] = ApiEnumDropdown.uuid_of_enum(
|
||||
# enum_class="BuildDuesTypes", key="R"
|
||||
# )
|
||||
# paid_value = (
|
||||
# DecisionBookProjectPaymentsMaster.pay_law_and_ren_of_build_part(
|
||||
# **payment_dict
|
||||
# )
|
||||
# )
|
||||
# print("is all dues_type", payment_dict["dues_type"], paid_value)
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class BuildIbans(CrudCollection):
|
||||
"""
|
||||
BuildParts class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_ibans"
|
||||
__exclude__fields__ = []
|
||||
|
||||
iban: Mapped[str] = mapped_column(
|
||||
String(40), server_default="", nullable=False, comment="IBAN number"
|
||||
)
|
||||
start_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Bank Transaction Start Date"
|
||||
)
|
||||
stop_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="2900-01-01 00:00:00"
|
||||
)
|
||||
bank_code: Mapped[str] = mapped_column(String(24), server_default="TR0000000000000")
|
||||
xcomment: Mapped[str] = mapped_column(String(64), server_default="????")
|
||||
build_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build.id"), nullable=True, comment="Building ID"
|
||||
)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Building UUID", index=True
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_build_ibans_ndx_01", iban, start_date, unique=True),
|
||||
{"comment": "IBANs related to money transactions due to building objects"},
|
||||
)
|
||||
|
||||
|
||||
class BuildIbanDescription(CrudCollection):
|
||||
"""
|
||||
SearchComments class based on declarative_base and CrudCollection via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_iban_description"
|
||||
__exclude__fields__ = []
|
||||
|
||||
iban: Mapped[str] = mapped_column(String, nullable=False, comment="IBAN Number")
|
||||
group_id: Mapped[int] = mapped_column(
|
||||
SmallInteger, nullable=False, comment="Group ID"
|
||||
)
|
||||
search_word: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Search Word", index=True
|
||||
)
|
||||
customer_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
customer_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Customer UUID"
|
||||
)
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UUID"
|
||||
)
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=True
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Parts UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_search_iban_description_ndx_00", iban, search_word, group_id, unique=True
|
||||
),
|
||||
{"comment": "Search Iban Description Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,575 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
BigInteger,
|
||||
Text,
|
||||
)
|
||||
|
||||
class RelationshipEmployee2PostCode(CrudCollection):
|
||||
"""
|
||||
Build2EmployeeRelationship class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "relationship_employee2postcode"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
) # 1, 2, 3
|
||||
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
|
||||
member_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_postcode.id"), nullable=False
|
||||
)
|
||||
|
||||
relationship_type: Mapped[str] = mapped_column(
|
||||
String, nullable=True, server_default="Employee"
|
||||
) # Commercial
|
||||
show_only: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
__table_args__ = ({"comment": "Build2Employee Relationship Information"},)
|
||||
|
||||
|
||||
class AddressPostcode(CrudCollection):
|
||||
"""
|
||||
Postcode class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_postcode"
|
||||
__exclude__fields__ = []
|
||||
__access_by__ = []
|
||||
__many__table__ = RelationshipEmployee2PostCode
|
||||
|
||||
street_id: Mapped[int] = mapped_column(ForeignKey("address_street.id"))
|
||||
street_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Street UUID"
|
||||
)
|
||||
postcode: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, comment="Postcode"
|
||||
)
|
||||
|
||||
__table_args__ = ({"comment": "Postcode Information"},)
|
||||
|
||||
|
||||
class Addresses(CrudCollection):
|
||||
"""
|
||||
Address class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "addresses"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_number: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, comment="Build Number"
|
||||
)
|
||||
door_number: Mapped[str] = mapped_column(
|
||||
String(24), nullable=True, comment="Door Number"
|
||||
)
|
||||
floor_number: Mapped[str] = mapped_column(
|
||||
String(24), nullable=True, comment="Floor Number"
|
||||
)
|
||||
|
||||
comment_address: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Address"
|
||||
)
|
||||
letter_address: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Address"
|
||||
)
|
||||
short_letter_address: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Address"
|
||||
)
|
||||
|
||||
latitude: Mapped[float] = mapped_column(Numeric(20, 12), server_default="0")
|
||||
longitude: Mapped[float] = mapped_column(Numeric(20, 12), server_default="0")
|
||||
|
||||
street_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_street.id"), nullable=False
|
||||
)
|
||||
street_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Street UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
# Index("_address_ndx_00", country_code, b_state, city, district),
|
||||
{"comment": "Address Information"},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def list_via_employee(cls, token_dict, filter_expr=None):
|
||||
with cls.new_session() as db_session:
|
||||
post_code_list = RelationshipEmployee2PostCode.filter_all(
|
||||
RelationshipEmployee2PostCode.employee_id
|
||||
== token_dict.selected_company.employee_id,
|
||||
db=db_session,
|
||||
).data
|
||||
post_code_id_list = [post_code.member_id for post_code in post_code_list]
|
||||
if not post_code_id_list:
|
||||
raise ValueError(
|
||||
"User has no post code registered. User can not list addresses."
|
||||
)
|
||||
# raise HTTPException(
|
||||
# status_code=404,
|
||||
# detail="User has no post code registered. User can not list addresses.",
|
||||
# )
|
||||
cls.pre_query = cls.filter_all(
|
||||
cls.post_code_id.in_(post_code_id_list), db=db_session
|
||||
).query
|
||||
filter_cls = cls.filter_all(*filter_expr or [], db=db_session)
|
||||
cls.pre_query = None
|
||||
return filter_cls.data
|
||||
|
||||
# buildings: Mapped["Build"] = relationship(
|
||||
# "Build", back_populates="addresses", foreign_keys="Build.address_id"
|
||||
# )
|
||||
# site: Mapped["BuildSites"] = relationship(
|
||||
# "BuildSites", back_populates="addresses", foreign_keys="BuildSites.address_id"
|
||||
# )
|
||||
# official_companies: Mapped["Companies"] = relationship(
|
||||
# "Company",
|
||||
# back_populates="official_address",
|
||||
# foreign_keys="Company.official_address_id",
|
||||
# )
|
||||
|
||||
# @classmethod
|
||||
# def create_action(cls, request, create_address: InsertAddress):
|
||||
# from services.redis.auth_actions.token import parse_token_object_to_dict
|
||||
#
|
||||
# token_dict = parse_token_object_to_dict(request=request)
|
||||
# data_dict = create_address.model_dump()
|
||||
# post_code = AddressPostcode.find_one(uu_id=create_address.post_code_uu_id)
|
||||
# if not post_code:
|
||||
# raise HTTPException(
|
||||
# status_code=404,
|
||||
# detail="Post code not found.",
|
||||
# )
|
||||
# if Employee2AddressRelationship.post_code_id.find_one(
|
||||
# employee_id=token_dict.selected_company.employee_id,
|
||||
# post_code_id=post_code.id,
|
||||
# ):
|
||||
# data_dict["post_code_id"] = post_code.id
|
||||
# del data_dict["post_code_uu_id"]
|
||||
# return cls.find_or_create(**create_address.model_dump())
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
# detail=f"User is not qualified to create address at this post code {post_code.postcode}",
|
||||
# )
|
||||
|
||||
# __table_args__ = (
|
||||
# Index("_address_ndx_00", country_code, b_state, city, district),
|
||||
# {"comment": "Address Information"},
|
||||
# )
|
||||
|
||||
|
||||
class AddressGeographicLocations(CrudCollection):
|
||||
"""
|
||||
Country class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_geographic_locations"
|
||||
__exclude__fields__ = []
|
||||
|
||||
geo_table: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Address Table Name"
|
||||
)
|
||||
geo_id: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, comment="Address Table ID"
|
||||
)
|
||||
geo_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Geographic Location Name"
|
||||
)
|
||||
geo_latitude: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), server_default="0", comment="Geographic Location Name"
|
||||
)
|
||||
geo_longitude: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), server_default="0", comment="Geographic Location Latitude"
|
||||
)
|
||||
geo_altitude: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), server_default="0", comment="Geographic Location Longitude"
|
||||
)
|
||||
geo_description: Mapped[str] = mapped_column(
|
||||
Text, nullable=False, comment="Geographic Location Description"
|
||||
)
|
||||
geo_area_size: Mapped[float] = mapped_column(
|
||||
Numeric(20, 2),
|
||||
nullable=True,
|
||||
server_default="0",
|
||||
comment="Geographic Location Area Size",
|
||||
)
|
||||
geo_population: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Geographic Location Population"
|
||||
)
|
||||
# geo_geom_point = mapped_column(Geometry('POINT', srid=4326), nullable=True, comment="Geographic Location Points")
|
||||
# geo_geom_polygon = mapped_column(Geometry('POLYGON', srid=4326), nullable=True,
|
||||
# comment="Geographic Location Vector geographic information (polygon)")
|
||||
# geo_centroid = mapped_column( GEOMETRY(POINT, 4326), nullable=True,
|
||||
# comment="Geographic Location center of gravity of the region(points)")
|
||||
|
||||
__table_args__ = (
|
||||
Index("_address_geographic_locations_ndx_00", geo_table, geo_id),
|
||||
Index("_address_geographic_locations_ndx_01", geo_latitude, geo_longitude),
|
||||
{"comment": "Geographic Location Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressCountry(CrudCollection):
|
||||
"""
|
||||
Country class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_country"
|
||||
__exclude__fields__ = []
|
||||
|
||||
country_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="Country Code"
|
||||
)
|
||||
country_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Country Name"
|
||||
)
|
||||
money_code: Mapped[str] = mapped_column(
|
||||
String(12), nullable=True, comment="Money Code"
|
||||
)
|
||||
language: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Language Code"
|
||||
)
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_address_country_ndx_00", money_code),
|
||||
Index("_address_country_ndx_01", country_code, unique=True),
|
||||
{"comment": "Country Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressState(CrudCollection):
|
||||
"""
|
||||
State class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_state"
|
||||
__exclude__fields__ = []
|
||||
|
||||
state_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="State Code"
|
||||
)
|
||||
state_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="State Name"
|
||||
)
|
||||
licence_plate: Mapped[str] = mapped_column(
|
||||
String(24), nullable=True, comment="Sign Code"
|
||||
)
|
||||
phone_code: Mapped[str] = mapped_column(
|
||||
String(36), nullable=True, comment="Phone Code"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
country_id: Mapped[int] = mapped_column(ForeignKey("address_country.id"))
|
||||
country_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Country UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_address_state_ndx_01",
|
||||
country_id,
|
||||
state_code,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "State Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressCity(CrudCollection):
|
||||
"""
|
||||
City class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_city"
|
||||
__exclude__fields__ = []
|
||||
|
||||
city_code: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, comment="City Code"
|
||||
)
|
||||
city_name: Mapped[str] = mapped_column(String, nullable=False, comment="City Name")
|
||||
licence_plate: Mapped[str] = mapped_column(
|
||||
String(24), nullable=True, comment="Sign Code"
|
||||
)
|
||||
phone_code: Mapped[str] = mapped_column(
|
||||
String(36), nullable=True, comment="Phone Code"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
state_id: Mapped[int] = mapped_column(ForeignKey("address_state.id"))
|
||||
state_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="State UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_address_city_ndx_01",
|
||||
state_id,
|
||||
city_code,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "City Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressDistrict(CrudCollection):
|
||||
"""
|
||||
District class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_district"
|
||||
__exclude__fields__ = []
|
||||
|
||||
district_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="District Code"
|
||||
)
|
||||
district_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="District Name"
|
||||
)
|
||||
phone_code: Mapped[str] = mapped_column(
|
||||
String(36), nullable=True, comment="Phone Code"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
city_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_city.id"), nullable=False, comment="City ID"
|
||||
)
|
||||
city_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="City UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_address_district_ndx_01",
|
||||
city_id,
|
||||
district_code,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "District Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressLocality(CrudCollection):
|
||||
"""
|
||||
Locality class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_locality"
|
||||
__exclude__fields__ = []
|
||||
|
||||
locality_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="Locality Code"
|
||||
)
|
||||
locality_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Locality Name"
|
||||
)
|
||||
type_code: Mapped[str] = mapped_column(String, nullable=True, comment="Type Name")
|
||||
type_description: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Type Name"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
address_show: Mapped[bool] = mapped_column(Boolean, server_default="1")
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
district_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_district.id"), nullable=False, comment="District ID"
|
||||
)
|
||||
district_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="District UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_address_locality_ndx_01",
|
||||
district_id,
|
||||
locality_code,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Locality Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressNeighborhood(CrudCollection):
|
||||
"""
|
||||
Neighborhood class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_neighborhood"
|
||||
__exclude__fields__ = []
|
||||
|
||||
neighborhood_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="Neighborhood Code"
|
||||
)
|
||||
neighborhood_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Neighborhood Name"
|
||||
)
|
||||
type_code: Mapped[str] = mapped_column(String, nullable=True, comment="Type Name")
|
||||
type_description: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Type Name"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
address_show: Mapped[bool] = mapped_column(Boolean, server_default="1")
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
|
||||
district_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_district.id"), nullable=True, comment="District ID"
|
||||
)
|
||||
district_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="District UUID"
|
||||
)
|
||||
locality_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_locality.id"), nullable=True, comment="Locality ID"
|
||||
)
|
||||
locality_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Locality UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_address_neighborhood_ndx_01",
|
||||
locality_id,
|
||||
neighborhood_code,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Neighborhood Information"},
|
||||
)
|
||||
|
||||
|
||||
class AddressStreet(CrudCollection):
|
||||
"""
|
||||
Street class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "address_street"
|
||||
__exclude__fields__ = []
|
||||
|
||||
street_code: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="Street Code"
|
||||
)
|
||||
street_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Street Name"
|
||||
)
|
||||
type_code: Mapped[str] = mapped_column(String, nullable=True, comment="Type Name")
|
||||
type_description: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Type Name"
|
||||
)
|
||||
gov_code: Mapped[str] = mapped_column(
|
||||
String(128), nullable=True, comment="Government Code"
|
||||
)
|
||||
|
||||
address_geographic_id: Mapped[int] = mapped_column(
|
||||
BigInteger, nullable=True, comment="Address Geographic Id"
|
||||
)
|
||||
neighborhood_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("address_neighborhood.id"), nullable=False, comment="Neighborhood ID"
|
||||
)
|
||||
neighborhood_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Neighborhood UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_address_street_ndx_01", neighborhood_id, street_code, unique=True),
|
||||
{"comment": "Street Information"},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def search_address_text(cls, search_text, token_dict=None):
|
||||
field_dict = {
|
||||
"AddressStreet.uu_id": cls.uu_id,
|
||||
"AddressCountry.uu_id": AddressCountry.uu_id,
|
||||
"AddressState.uu_id": AddressState.uu_id,
|
||||
"AddressCity.uu_id": AddressCity.uu_id,
|
||||
"AddressDistrict.uu_id": AddressDistrict.uu_id,
|
||||
"AddressLocality.uu_id": AddressLocality.uu_id,
|
||||
"AddressNeighborhood.uu_id": AddressNeighborhood.uu_id,
|
||||
"AddressCountry.country_name": AddressCountry.country_name,
|
||||
"AddressState.state_name": AddressState.state_name,
|
||||
"AddressCity.city_name": AddressCity.city_name,
|
||||
"AddressDistrict.district_name": AddressDistrict.district_name,
|
||||
"AddressLocality.locality_name": AddressLocality.locality_name,
|
||||
"AddressNeighborhood.neighborhood_name": AddressNeighborhood.neighborhood_name,
|
||||
"AddressStreet.street_name": cls.street_name,
|
||||
}
|
||||
joined_data = (
|
||||
cls.session.query(*list(field_dict.values()))
|
||||
.select_from(cls)
|
||||
.join(AddressNeighborhood, AddressNeighborhood.id == cls.neighborhood_id)
|
||||
.join(
|
||||
AddressLocality, AddressLocality.id == AddressNeighborhood.locality_id
|
||||
)
|
||||
.join(AddressDistrict, AddressDistrict.id == AddressLocality.district_id)
|
||||
.join(AddressCity, AddressCity.id == AddressDistrict.city_id)
|
||||
.join(AddressState, AddressState.id == AddressCity.state_id)
|
||||
.join(AddressCountry, AddressCountry.id == AddressState.country_id)
|
||||
.filter(
|
||||
or_(
|
||||
AddressNeighborhood.neighborhood_name.ilike(
|
||||
f"%{str(search_text).upper()}%"
|
||||
),
|
||||
AddressLocality.locality_name.ilike(
|
||||
f"%{str(search_text).upper()}%"
|
||||
),
|
||||
AddressDistrict.district_name.ilike(
|
||||
f"%{str(search_text).upper()}%"
|
||||
),
|
||||
# AddressCity.city_name.ilike(f"%{str(search_text).upper()}%"),
|
||||
# AddressState.state_name.ilike(f"%{str(search_text).upper()}%"),
|
||||
# AddressCountry.country_name.ilike(f"%{str(search_text).upper()}%"),
|
||||
cls.street_name.ilike(f"%{str(search_text).upper()}%"),
|
||||
),
|
||||
)
|
||||
)
|
||||
# select([mytable.c.id]).where(
|
||||
# func.to_tsvector('english', mytable.c.title) \
|
||||
# .match('somestring', postgresql_regconfig='english')
|
||||
# )
|
||||
joined_statement = joined_data
|
||||
joined_data = joined_data.first()
|
||||
if not joined_data:
|
||||
raise ValueError(
|
||||
"No address found with the given search text.",
|
||||
)
|
||||
# raise HTTPException(
|
||||
# status_code=404,
|
||||
# detail="No address found with the given search text.",
|
||||
# )
|
||||
return dict(
|
||||
query=joined_statement,
|
||||
schema=list(field_dict.keys()),
|
||||
)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from api_controllers.postgres.mixin import CrudCollection
|
||||
from sqlalchemy.orm import mapped_column, Mapped, relationship
|
||||
from sqlalchemy import (
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
text,
|
||||
or_,
|
||||
and_,
|
||||
func,
|
||||
UUID,
|
||||
JSON,
|
||||
BigInteger,
|
||||
Float,
|
||||
Text,
|
||||
LargeBinary,
|
||||
PickleType,
|
||||
DATETIME,
|
||||
)
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
ForeignKey,
|
||||
Index,
|
||||
SmallInteger,
|
||||
TIMESTAMP,
|
||||
Text,
|
||||
Numeric,
|
||||
Integer,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
relationship,
|
||||
func,
|
||||
)
|
||||
|
||||
|
||||
class DecisionBookBudgetBooks(CrudCollection):
|
||||
|
||||
__tablename__ = "decision_book_budget_books"
|
||||
__exclude__fields__ = []
|
||||
|
||||
country: Mapped[str] = mapped_column(String, nullable=False)
|
||||
branch_type: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=False)
|
||||
company_uu_id: Mapped[str] = mapped_column(String, nullable=False)
|
||||
branch_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
branch_uu_id: Mapped[str] = mapped_column(
|
||||
String, comment="Branch UU ID", nullable=True
|
||||
)
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=False
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Decision Book UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_decision_book_budget_companies_book_ndx_00",
|
||||
company_id,
|
||||
"created_at",
|
||||
),
|
||||
{"comment": "budget Book Information"},
|
||||
)
|
||||
|
||||
|
||||
class DecisionBookBudgetCodes(CrudCollection):
|
||||
|
||||
__tablename__ = "decision_book_budget_codes"
|
||||
__exclude__fields__ = []
|
||||
|
||||
budget_code: Mapped[str] = mapped_column(
|
||||
String(48), nullable=False, comment="budget Code"
|
||||
)
|
||||
comment_line: Mapped[str] = mapped_column(
|
||||
Text, nullable=False, comment="Comment Line"
|
||||
)
|
||||
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=True
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Decision Book UU ID"
|
||||
)
|
||||
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=True
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Parts UU ID"
|
||||
)
|
||||
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_decision_book_budget_codes_ndx_00", budget_code, "created_at"),
|
||||
Index("_decision_book_budget_codes_ndx_01", company_id, "created_at"),
|
||||
{"comment": "budget Book Information"},
|
||||
)
|
||||
|
||||
|
||||
class DecisionBookBudgetMaster(CrudCollection):
|
||||
|
||||
__tablename__ = "decision_book_budget_master"
|
||||
__exclude__fields__ = []
|
||||
|
||||
budget_type: Mapped[str] = mapped_column(
|
||||
String(50), nullable=False
|
||||
) # Bütçe tipi (örneğin: Operasyonel, Yatırım)
|
||||
currency: Mapped[str] = mapped_column(
|
||||
String(8), server_default="TRY"
|
||||
) # Bütçe para birimi
|
||||
total_budget: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), nullable=False
|
||||
) # Toplam bütçe
|
||||
|
||||
tracking_period_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
tracking_period_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Part Direction UUID"
|
||||
)
|
||||
budget_books_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("decision_book_budget_books.id"), nullable=False
|
||||
)
|
||||
budget_books_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Budget Books UU ID"
|
||||
)
|
||||
department_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("departments.id"), nullable=False
|
||||
) # Departman ile ilişki
|
||||
department_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Department UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = ({"comment": "budget Book Information"},)
|
||||
|
||||
|
||||
class DecisionBookBudgets(CrudCollection):
|
||||
|
||||
__tablename__ = "decision_book_budgets"
|
||||
__exclude__fields__ = []
|
||||
|
||||
process_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False
|
||||
) # Başlangıç tarihi
|
||||
budget_codes_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("decision_book_budget_codes.id"), nullable=False
|
||||
)
|
||||
total_budget: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), nullable=False
|
||||
) # Toplam bütçe
|
||||
used_budget: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), nullable=False, default=0.0
|
||||
) # Kullanılan bütçe
|
||||
remaining_budget: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), nullable=False, default=0.0
|
||||
) # Kullanılan bütçe
|
||||
|
||||
decision_book_budget_master_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("decision_book_budget_master.id"), nullable=False
|
||||
)
|
||||
decision_book_budget_master_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Budget Master UU ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_decision_book_budgets_ndx_00",
|
||||
decision_book_budget_master_uu_id,
|
||||
process_date,
|
||||
),
|
||||
{"comment": "budget Book Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,602 @@
|
|||
import arrow
|
||||
from datetime import timedelta
|
||||
from typing import List, Union, Any
|
||||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
relationship,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Text,
|
||||
Numeric,
|
||||
or_,
|
||||
and_,
|
||||
)
|
||||
|
||||
|
||||
class BuildTypes(CrudCollection):
|
||||
"""
|
||||
BuildTypes class based on declarative_base and BaseMixin via session
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = "build_types"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
function_code: Mapped[str] = mapped_column(
|
||||
String(12), server_default="", nullable=False, comment="Function Code"
|
||||
)
|
||||
type_code: Mapped[str] = mapped_column(
|
||||
String(12), server_default="", nullable=False, comment="Structure Type Code"
|
||||
)
|
||||
lang: Mapped[str] = mapped_column(
|
||||
String(4), server_default="TR", nullable=False, comment="Language"
|
||||
)
|
||||
type_name: Mapped[str] = mapped_column(
|
||||
String(48), server_default="", nullable=False, comment="Type Name"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_build_types_ndx_00", type_code, function_code, lang, unique=True),
|
||||
{"comment": "Function group of building types with their language information"},
|
||||
)
|
||||
|
||||
|
||||
class Part2Employee(CrudCollection):
|
||||
"""
|
||||
Employee2Parts class based on declarative_base and BaseMixin via session
|
||||
In between start and end date, a part can be assigned to only one employee
|
||||
"""
|
||||
|
||||
__tablename__ = "part2employee"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
build_id: Mapped[int] = mapped_column(Integer, comment="Building ID")
|
||||
part_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=False, comment="Part ID"
|
||||
)
|
||||
employee_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("employees.id"), nullable=False, comment="Employee ID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_part2employee_ndx_00", employee_id, part_id, unique=True),
|
||||
{"comment": "Employee2Parts Information"},
|
||||
)
|
||||
|
||||
|
||||
class RelationshipEmployee2Build(CrudCollection):
|
||||
"""
|
||||
CompanyRelationship class based on declarative_base and CrudCollection via session
|
||||
Company -> Sub Company -> Sub-Sub Company
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = "relationship_employee2build"
|
||||
__exclude__fields__ = []
|
||||
|
||||
company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=False
|
||||
) # 1, 2, 3
|
||||
employee_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("employees.id"), nullable=False
|
||||
) # employee -> (n)person Evyos LTD
|
||||
member_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build.id"), nullable=False
|
||||
) # 2, 3, 4
|
||||
|
||||
relationship_type: Mapped[str] = mapped_column(
|
||||
String, nullable=True, server_default="Employee"
|
||||
) # Commercial
|
||||
show_only: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"relationship_build_employee_ndx_00",
|
||||
company_id,
|
||||
employee_id,
|
||||
member_id,
|
||||
relationship_type,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Build & Employee Relationship Information"},
|
||||
)
|
||||
|
||||
|
||||
class Build(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
__access_by__ = []
|
||||
# __many__table__ = RelationshipEmployee2Build
|
||||
|
||||
gov_address_code: Mapped[str] = mapped_column(String, server_default="", unique=True)
|
||||
build_name: Mapped[str] = mapped_column(String, nullable=False, comment="Building Name")
|
||||
build_no: Mapped[str] = mapped_column(String(8), nullable=False, comment="Building Number")
|
||||
|
||||
max_floor: Mapped[int] = mapped_column(Integer, server_default="1", nullable=False, comment="Max Floor")
|
||||
underground_floor: Mapped[int] = mapped_column(Integer, server_default="0", nullable=False, comment="Underground Floor")
|
||||
build_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), server_default="1900-01-01")
|
||||
decision_period_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True),
|
||||
server_default="1900-01-01",
|
||||
comment="Building annual ordinary meeting period",
|
||||
)
|
||||
tax_no: Mapped[str] = mapped_column(String(24), server_default="")
|
||||
lift_count: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
heating_system: Mapped[bool] = mapped_column(Boolean, server_default="True")
|
||||
cooling_system: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
hot_water_system: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
block_service_man_count: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
security_service_man_count: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
garage_count: Mapped[int] = mapped_column(Integer, server_default="0", comment="Garage Count")
|
||||
management_room_id: Mapped[int] = mapped_column(Integer, nullable=True, comment="Management Room ID")
|
||||
|
||||
site_id: Mapped[int] = mapped_column(ForeignKey("build_sites.id"), nullable=True)
|
||||
site_uu_id: Mapped[str] = mapped_column(String, comment="Site UUID", nullable=True)
|
||||
address_id: Mapped[int] = mapped_column(ForeignKey("addresses.id"), nullable=False)
|
||||
address_uu_id: Mapped[str] = mapped_column(String, comment="Address UUID", nullable=False)
|
||||
build_types_id: Mapped[int] = mapped_column(ForeignKey("build_types.id"), nullable=False, comment="Building Type")
|
||||
build_types_uu_id: Mapped[str] = mapped_column(String, comment="Building Type UUID")
|
||||
|
||||
parts: Mapped[List["BuildParts"]] = relationship("BuildParts", back_populates="buildings", foreign_keys="BuildParts.build_id")
|
||||
decision_books: Mapped[List["BuildDecisionBook"]] = relationship(
|
||||
"BuildDecisionBook",
|
||||
back_populates="buildings",
|
||||
foreign_keys="BuildDecisionBook.build_id",
|
||||
)
|
||||
|
||||
# build_ibans: Mapped["BuildIbans"] = relationship(
|
||||
# "BuildIbans", back_populates="building", foreign_keys="BuildIbans.build_id"
|
||||
# )
|
||||
# areas: Mapped["BuildArea"] = relationship(
|
||||
# "BuildArea", back_populates="buildings", foreign_keys="BuildArea.build_id"
|
||||
# )
|
||||
# response_companies: Mapped["Companies"] = relationship(
|
||||
# "Companies",
|
||||
# back_populates="response_buildings",
|
||||
# foreign_keys=[response_company_id],
|
||||
# )
|
||||
# addresses: Mapped[List["Address"]] = relationship(
|
||||
# "Address", back_populates="buildings", foreign_keys=[address_id]
|
||||
# )
|
||||
# peoples: Mapped["People"] = relationship(
|
||||
# "People", back_populates="buildings", foreign_keys=[people_id]
|
||||
# )
|
||||
# sites: Mapped["BuildSites"] = relationship(
|
||||
# "BuildSites", back_populates="buildings", foreign_keys=[site_id]
|
||||
# )
|
||||
|
||||
__table_args__ = (
|
||||
Index("_builds_ndx_00", gov_address_code),
|
||||
Index("_builds_ndx_01", build_name, build_no),
|
||||
{
|
||||
"comment": "Build objects are building that are created for living and store purposes"
|
||||
},
|
||||
)
|
||||
|
||||
@property
|
||||
def management_room(self):
|
||||
with self.new_session() as db_session:
|
||||
if management_room := BuildParts.filter_by_one(
|
||||
system=True, id=self.management_room_id, build_id=self.id, db=db_session
|
||||
).data:
|
||||
return management_room
|
||||
return None
|
||||
|
||||
@property
|
||||
def top_flat(self):
|
||||
max_flat_no = 0
|
||||
for part in self.parts:
|
||||
if part.part_no > self.max_floor:
|
||||
max_flat_no = part.part_no
|
||||
return max_flat_no
|
||||
|
||||
@property
|
||||
def bottom_flat(self):
|
||||
min_flat_no = 0
|
||||
for part in self.parts:
|
||||
if part.part_no < self.max_floor:
|
||||
min_flat_no = part.part_no
|
||||
return min_flat_no
|
||||
|
||||
@property
|
||||
def human_livable_parts(self) -> tuple:
|
||||
parts = list(part for part in self.parts if part.human_livable)
|
||||
return parts, len(parts)
|
||||
|
||||
@property
|
||||
def livable_part_count(self):
|
||||
with self.new_session() as db_session:
|
||||
livable_parts = BuildParts.filter_all(
|
||||
BuildParts.build_id == self.id,
|
||||
BuildParts.human_livable == True,
|
||||
db=db_session,
|
||||
)
|
||||
if not livable_parts.data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="There is no livable part in this building.",
|
||||
)
|
||||
return livable_parts.count
|
||||
|
||||
@property
|
||||
def part_type_count(self):
|
||||
with self.new_session() as db_session:
|
||||
building_types = None
|
||||
for part in self.parts:
|
||||
building_types = {}
|
||||
build_type = BuildTypes.filter_by_one(
|
||||
system=True, id=part.build_part_type_id, db=db_session
|
||||
).data
|
||||
if build_type.type_code in building_types:
|
||||
building_types[build_type.type_code]["list"].append(part.part_no)
|
||||
else:
|
||||
building_types[build_type.type_code] = {"list": [part.part_no]}
|
||||
|
||||
# for key, val in building_types.items():
|
||||
# list_parts = val["list"]
|
||||
# building_types[key] = {
|
||||
# "list": list_parts,
|
||||
# "min": min(list_parts),
|
||||
# "max": max(list_parts),
|
||||
# "count": len(list_parts),
|
||||
# }
|
||||
return building_types
|
||||
|
||||
|
||||
class BuildParts(CrudCollection):
|
||||
"""
|
||||
BuildParts class based on declarative_base and BaseMixin via session
|
||||
Attentions: Part_no is unique for each building and Every building must have a management section.!!! default no 0
|
||||
"""
|
||||
|
||||
__tablename__ = "build_parts"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
__enum_list__ = [("part_direction", "Directions", "NN")]
|
||||
|
||||
# https://adres.nvi.gov.tr/VatandasIslemleri/AdresSorgu
|
||||
address_gov_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Goverment Door Code"
|
||||
)
|
||||
# part_name: Mapped[str] = mapped_column(String(24), server_default="", nullable=False, comment="Part Name")
|
||||
part_no: Mapped[int] = mapped_column(
|
||||
Integer, server_default="0", nullable=False, comment="Part Number"
|
||||
)
|
||||
part_level: Mapped[int] = mapped_column(
|
||||
Integer, server_default="0", comment="Building Part Level"
|
||||
)
|
||||
part_code: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=False, comment="Part Code"
|
||||
)
|
||||
part_gross_size: Mapped[int] = mapped_column(
|
||||
Integer, server_default="0", comment="Part Gross Size"
|
||||
)
|
||||
part_net_size: Mapped[int] = mapped_column(
|
||||
Integer, server_default="0", comment="Part Net Size"
|
||||
)
|
||||
default_accessory: Mapped[str] = mapped_column(
|
||||
Text, server_default="0", comment="Default Accessory"
|
||||
)
|
||||
human_livable: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="1", comment="Human Livable"
|
||||
)
|
||||
due_part_key: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=False, comment="Constant Payment Group"
|
||||
)
|
||||
|
||||
build_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build.id"), nullable=False, comment="Building ID"
|
||||
)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Building UUID"
|
||||
)
|
||||
part_direction_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
part_direction_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Part Direction UUID"
|
||||
)
|
||||
part_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_types.id"), nullable=False, comment="Building Part Type"
|
||||
)
|
||||
part_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Building Part Type UUID"
|
||||
)
|
||||
|
||||
buildings: Mapped["Build"] = relationship(
|
||||
"Build", back_populates="parts", foreign_keys=[build_id]
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("build_parts_ndx_1", build_id, part_no, unique=True),
|
||||
{"comment": "Part objects that are belong to building objects"},
|
||||
)
|
||||
|
||||
def part_name(self, db):
|
||||
if build_type := BuildTypes.filter_by_one(
|
||||
system=True, id=self.part_type_id, db=db
|
||||
).data:
|
||||
return f"{str(build_type.type_name).upper()} : {str(self.part_no).upper()}"
|
||||
return f"Undefined:{str(build_type.type_name).upper()}"
|
||||
|
||||
|
||||
class BuildLivingSpace(CrudCollection):
|
||||
"""
|
||||
LivingSpace class based on declarative_base and BaseMixin via session
|
||||
Owner or live person = Occupant of the build part
|
||||
+ Query OR(owner_person_id == person_id, life_person_id == person_id) AND (now(date))
|
||||
"""
|
||||
|
||||
__tablename__ = "build_living_space"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
fix_value: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6),
|
||||
server_default="0",
|
||||
comment="Fixed value is deducted from debit.",
|
||||
)
|
||||
fix_percent: Mapped[float] = mapped_column(
|
||||
Numeric(6, 2),
|
||||
server_default="0",
|
||||
comment="Fixed percent is deducted from debit.",
|
||||
)
|
||||
|
||||
agreement_no: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Agreement No"
|
||||
)
|
||||
marketing_process: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
marketing_layer: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="Build Part ID",
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Part UUID"
|
||||
)
|
||||
person_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="Responsible People ID",
|
||||
)
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Responsible People UUID"
|
||||
)
|
||||
occupant_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("occupant_types.id"),
|
||||
nullable=False,
|
||||
comment="Occupant Type",
|
||||
)
|
||||
occupant_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Occupant Type UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
{"comment": "Living Space inside building parts that are related to people"},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def find_living_from_customer_id(
|
||||
cls, customer_id, process_date, add_days: int = 32
|
||||
):
|
||||
with cls.new_session() as db_session:
|
||||
formatted_date = arrow.get(str(process_date))
|
||||
living_spaces = cls.filter_all(
|
||||
or_(
|
||||
cls.owner_person_id == customer_id,
|
||||
cls.life_person_id == customer_id,
|
||||
),
|
||||
cls.start_date < formatted_date - timedelta(days=add_days),
|
||||
cls.stop_date > formatted_date + timedelta(days=add_days),
|
||||
db=db_session,
|
||||
)
|
||||
return living_spaces.data, living_spaces.count
|
||||
|
||||
|
||||
class BuildManagement(CrudCollection):
|
||||
|
||||
__tablename__ = "build_management"
|
||||
__exclude__fields__ = []
|
||||
|
||||
discounted_percentage: Mapped[float] = mapped_column(
|
||||
Numeric(6, 2), server_default="0.00"
|
||||
) # %22
|
||||
discounted_price: Mapped[float] = mapped_column(
|
||||
Numeric(20, 2), server_default="0.00"
|
||||
) # Normal: 78.00 TL
|
||||
calculated_price: Mapped[float] = mapped_column(
|
||||
Numeric(20, 2), server_default="0.00"
|
||||
) # sana düz 75.00 TL yapar
|
||||
|
||||
occupant_type: Mapped[int] = mapped_column(
|
||||
ForeignKey("occupant_types.id"),
|
||||
nullable=False,
|
||||
comment="Occupant Type",
|
||||
)
|
||||
occupant_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Occupant Type UUID"
|
||||
)
|
||||
build_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build.id"), nullable=False, comment="Building ID"
|
||||
)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Building UUID"
|
||||
)
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="Build Part ID",
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Part UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"build_management_ndx_00",
|
||||
build_parts_id,
|
||||
occupant_type,
|
||||
"expiry_starts",
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Management of the building parts that are related to people"},
|
||||
)
|
||||
|
||||
|
||||
class BuildArea(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_area"
|
||||
__exclude__fields__ = []
|
||||
|
||||
area_name: Mapped[str] = mapped_column(String, server_default="")
|
||||
area_code: Mapped[str] = mapped_column(String, server_default="")
|
||||
area_type: Mapped[str] = mapped_column(String, server_default="GREEN")
|
||||
area_direction: Mapped[str] = mapped_column(String(2), server_default="NN")
|
||||
area_gross_size: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
area_net_size: Mapped[float] = mapped_column(Numeric(20, 6), server_default="0")
|
||||
width = mapped_column(Integer, server_default="0")
|
||||
size = mapped_column(Integer, server_default="0")
|
||||
|
||||
build_id: Mapped[int] = mapped_column(ForeignKey("build.id"))
|
||||
build_uu_id: Mapped[str] = mapped_column(String, comment="Building UUID")
|
||||
part_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_types.id"), nullable=True, comment="Building Part Type"
|
||||
)
|
||||
part_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Building Part Type UUID"
|
||||
)
|
||||
|
||||
# buildings: Mapped["Build"] = relationship(
|
||||
# "Build", back_populates="areas", foreign_keys=[build_id]
|
||||
# )
|
||||
|
||||
_table_args_ = (
|
||||
Index("_edm_build_parts_area_ndx_00", build_id, area_code, unique=True),
|
||||
)
|
||||
|
||||
|
||||
class BuildSites(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_sites"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
site_name: Mapped[str] = mapped_column(String(24), nullable=False)
|
||||
site_no: Mapped[str] = mapped_column(String(8), nullable=False)
|
||||
|
||||
address_id: Mapped[int] = mapped_column(ForeignKey("addresses.id"))
|
||||
address_uu_id: Mapped[str] = mapped_column(String, comment="Address UUID")
|
||||
|
||||
# addresses: Mapped["Address"] = relationship(
|
||||
# "Address", back_populates="site", foreign_keys=[address_id]
|
||||
# )
|
||||
# buildings: Mapped["Build"] = relationship(
|
||||
# "Build", back_populates="sites", foreign_keys="Build.site_id"
|
||||
# )
|
||||
|
||||
__table_args__ = (
|
||||
Index("_sites_ndx_01", site_no, site_name),
|
||||
{"comment": "Sites that groups building objets"},
|
||||
)
|
||||
|
||||
|
||||
class BuildCompaniesProviding(CrudCollection):
|
||||
""" """
|
||||
|
||||
__tablename__ = "build_companies_providing"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
build_id = mapped_column(
|
||||
ForeignKey("build.id"), nullable=False, comment="Building ID"
|
||||
)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Providing UUID"
|
||||
)
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Providing UUID"
|
||||
)
|
||||
provide_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
provide_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Providing UUID"
|
||||
)
|
||||
contract_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_companies_providing_ndx_00",
|
||||
build_id,
|
||||
company_id,
|
||||
provide_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Companies providing services for building"},
|
||||
)
|
||||
|
||||
|
||||
class BuildPersonProviding(CrudCollection):
|
||||
""" """
|
||||
|
||||
__tablename__ = "build_person_providing"
|
||||
__exclude__fields__ = []
|
||||
__include__fields__ = []
|
||||
|
||||
build_id = mapped_column(
|
||||
ForeignKey("build.id"), nullable=False, comment="Building ID"
|
||||
)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Providing UUID"
|
||||
)
|
||||
people_id: Mapped[int] = mapped_column(ForeignKey("people.id"))
|
||||
people_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="People UUID"
|
||||
)
|
||||
provide_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
provide_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Providing UUID"
|
||||
)
|
||||
contract_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_person_providing_ndx_00",
|
||||
build_id,
|
||||
people_id,
|
||||
provide_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "People providing services for building"},
|
||||
)
|
||||
|
|
@ -0,0 +1,888 @@
|
|||
import math
|
||||
import arrow
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from typing import List, Any
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
ForeignKey,
|
||||
Index,
|
||||
SmallInteger,
|
||||
Boolean,
|
||||
TIMESTAMP,
|
||||
Text,
|
||||
Numeric,
|
||||
Integer,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
relationship,
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBook(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
The start dates of the decision log periods are determined from the 'decision_period_date' field in the decision log table within the building information.
|
||||
decision_period_date = Her yıl yapılan karar toplantısı + 365 gün her yıl tekrar eden
|
||||
decision_book_pdf_path: Karar defteri pdf dosyasının yolu
|
||||
resp_company_fix_wage: Karar defterinin oluşmasını sağlayan dışardaki danışmanlık ücreti
|
||||
is_out_sourced: Karar defterinin dışardan alınan hizmetle oluşturulup oluşturulmadığı
|
||||
contact_agreement_path: Karar defterinin oluşmasını sağlayan dışardaki danışmanlık anlaşması dosyasının yolu
|
||||
contact_agreement_date: Karar defterinin oluşmasını sağlayan dışardaki danışmanlık anlaşma tarihi
|
||||
meeting_date: Karar defterinin oluşmasını sağlayan toplantı tarihi
|
||||
decision_type: Karar defterinin tipi (Bina Yönetim Toplantısı (BYT), Yıllık Acil Toplantı (YAT)
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book"
|
||||
__exclude__fields__ = []
|
||||
|
||||
decision_book_pdf_path: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=True
|
||||
)
|
||||
resp_company_fix_wage: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), server_default="0"
|
||||
) #
|
||||
is_out_sourced: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
meeting_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01"
|
||||
)
|
||||
decision_type: Mapped[str] = mapped_column(String(3), server_default="RBM")
|
||||
meeting_is_completed: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
meeting_completed_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=True, comment="Meeting Completed Date"
|
||||
)
|
||||
|
||||
build_id: Mapped[int] = mapped_column(ForeignKey("build.id"), nullable=False)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build UUID"
|
||||
)
|
||||
resp_company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
resp_company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UUID"
|
||||
)
|
||||
contact_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("contracts.id"), nullable=True, comment="Contract id"
|
||||
)
|
||||
contact_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Contract UUID"
|
||||
)
|
||||
|
||||
buildings: Mapped["Build"] = relationship(
|
||||
"Build",
|
||||
back_populates="decision_books",
|
||||
foreign_keys=build_id,
|
||||
)
|
||||
decision_book_items: Mapped[List["BuildDecisionBookItems"]] = relationship(
|
||||
"BuildDecisionBookItems",
|
||||
back_populates="decision_books",
|
||||
foreign_keys="BuildDecisionBookItems.build_decision_book_id",
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("build_decision_book_ndx_011", meeting_date, build_id),
|
||||
Index("build_decision_book_ndx_011", build_id, "expiry_starts", "expiry_ends"),
|
||||
{
|
||||
"comment": "Decision Book objects that are related to decision taken at building meetings"
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def retrieve_active_rbm(cls):
|
||||
from Schemas.building.build import Build
|
||||
|
||||
with cls.new_session() as db_session:
|
||||
related_build = Build.find_one(id=cls.build_id)
|
||||
related_date = arrow.get(related_build.build_date)
|
||||
date_processed = related_date.replace(
|
||||
year=arrow.now().date().year, month=related_date.month, day=1
|
||||
)
|
||||
if arrow.now().date() <= date_processed:
|
||||
book = cls.filter_one(
|
||||
cls.expiry_ends <= date_processed,
|
||||
cls.decision_type == "RBM",
|
||||
cls.build_id == related_build.id,
|
||||
db=db_session,
|
||||
).data
|
||||
if not book:
|
||||
cls.raise_http_exception(
|
||||
status_code="HTTP_404_NOT_FOUND",
|
||||
error_case="NOTFOUND",
|
||||
message=f"Decision Book is not found for {related_build.build_name}-RBM",
|
||||
data=dict(
|
||||
build_id=str(related_build.uu_id),
|
||||
build_name=related_build.build_name,
|
||||
decision_type="RBM",
|
||||
),
|
||||
)
|
||||
return book
|
||||
return
|
||||
|
||||
@property
|
||||
def semester(self):
|
||||
start_format = "".join(
|
||||
[str(self.expiry_starts.year), "-", str(self.expiry_starts.month)]
|
||||
)
|
||||
end_format = "".join(
|
||||
[str(self.expiry_ends.year), "-", str(self.expiry_ends.month)]
|
||||
)
|
||||
return "".join([start_format, " ", end_format])
|
||||
|
||||
def check_book_is_valid(self, bank_date: str):
|
||||
if all(
|
||||
[True if letter in str(bank_date) else False for letter in ["-", " ", ":"]]
|
||||
):
|
||||
bank_date = datetime.strptime(str(bank_date), "%Y-%m-%d %H:%M:%S")
|
||||
date_valid = (
|
||||
arrow.get(self.expiry_starts)
|
||||
< arrow.get(bank_date)
|
||||
< arrow.get(self.expiry_ends)
|
||||
)
|
||||
return date_valid and self.active and not self.deleted
|
||||
|
||||
# @classmethod
|
||||
# def retrieve_valid_book(cls, bank_date, iban):
|
||||
# from Schemas import (
|
||||
# BuildIbans,
|
||||
# )
|
||||
# with cls.new_session() as db_session:
|
||||
# if all(
|
||||
# [True if letter in str(bank_date) else False for letter in ["-", " ", ":"]]
|
||||
# ):
|
||||
# bank_date = datetime.strptime(str(bank_date), "%Y-%m-%d %H:%M:%S")
|
||||
# build_iban = BuildIbans.find_one(iban=iban)
|
||||
# decision_book: cls = cls.filter_one(
|
||||
# cls.build_id == build_iban.build_id,
|
||||
# cls.expiry_starts < bank_date,
|
||||
# cls.expiry_ends > bank_date,
|
||||
# cls.active == True,
|
||||
# cls.deleted == False,
|
||||
# db=db_session
|
||||
# ).data
|
||||
# decision_book.check_book_is_valid(bank_date.__str__())
|
||||
# return decision_book
|
||||
# return
|
||||
|
||||
|
||||
class BuildDecisionBookInvitations(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_invitations"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build UUID"
|
||||
)
|
||||
decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=False
|
||||
)
|
||||
decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book UUID"
|
||||
)
|
||||
|
||||
invitation_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Invite Type"
|
||||
)
|
||||
invitation_attempt: Mapped[int] = mapped_column(SmallInteger, server_default="1")
|
||||
living_part_count: Mapped[int] = mapped_column(SmallInteger, server_default="1")
|
||||
living_part_percentage: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), server_default="0.51"
|
||||
)
|
||||
|
||||
message: Mapped[str] = mapped_column(
|
||||
Text, nullable=True, comment="Invitation Message"
|
||||
)
|
||||
planned_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Planned Meeting Date"
|
||||
)
|
||||
planned_date_expires: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Planned Meeting Date Expires"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_decision_book_invitations_ndx_01",
|
||||
invitation_type,
|
||||
planned_date,
|
||||
invitation_attempt,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "People that are invited to building meetings."},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check_invites_are_ready_for_meeting(cls, selected_decision_book, token_dict):
|
||||
with cls.new_session() as db_session:
|
||||
first_book_invitation = BuildDecisionBookInvitations.filter_one(
|
||||
BuildDecisionBookInvitations.build_id
|
||||
== token_dict.selected_occupant.build_id,
|
||||
BuildDecisionBookInvitations.decision_book_id
|
||||
== selected_decision_book.id,
|
||||
BuildDecisionBookInvitations.invitation_attempt == 1,
|
||||
db=db_session,
|
||||
).data
|
||||
if not first_book_invitation:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"First Meeting Invitation is not found for Decision Book UUID : {selected_decision_book.uu_id}",
|
||||
)
|
||||
need_attend_count = int(first_book_invitation.living_part_count) * Decimal(
|
||||
first_book_invitation.living_part_percentage
|
||||
)
|
||||
valid_invite_count = (
|
||||
BuildDecisionBookPerson.filter_all_system(
|
||||
BuildDecisionBookPerson.invite_id == first_book_invitation.id,
|
||||
BuildDecisionBookPerson.build_decision_book_id
|
||||
== selected_decision_book.id,
|
||||
BuildDecisionBookPerson.is_attending == True,
|
||||
db=db_session,
|
||||
)
|
||||
.query.distinct(BuildDecisionBookPerson.person_id)
|
||||
.count()
|
||||
)
|
||||
|
||||
second_book_invitation = BuildDecisionBookInvitations.filter_one_system(
|
||||
BuildDecisionBookInvitations.build_id
|
||||
== token_dict.selected_occupant.build_id,
|
||||
BuildDecisionBookInvitations.decision_book_id
|
||||
== selected_decision_book.id,
|
||||
BuildDecisionBookInvitations.invitation_attempt == 2,
|
||||
db=db_session,
|
||||
).data
|
||||
if (
|
||||
not valid_invite_count >= need_attend_count
|
||||
and not second_book_invitation
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"In order meeting to be held, {math.ceil(need_attend_count)} people must attend "
|
||||
f"to the meeting. Only {valid_invite_count} people are attending to the meeting.",
|
||||
)
|
||||
return first_book_invitation or second_book_invitation
|
||||
|
||||
|
||||
class BuildDecisionBookPerson(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
Karar Defteri toplantılarına katılan kişiler veya yetkililer
|
||||
dues_percent_discount: Katılımcının aidat indirim oranı Aidatdan yüzde indirim alır
|
||||
dues_fix_discount: Katılımcının aidat sabit miktarı Aidatdan sabit bir miktar indirim alır
|
||||
dues_discount_approval_date: Bu kişinin indiriminin onayladığı tarih
|
||||
management_typecode: Kişinin toplantı görevi
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_person"
|
||||
__exclude__fields__ = []
|
||||
__enum_list__ = [("management_typecode", "BuildManagementType", "bm")]
|
||||
|
||||
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
dues_fix_discount: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
|
||||
dues_discount_approval_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00"
|
||||
)
|
||||
send_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Confirmation Date"
|
||||
)
|
||||
is_attending: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Occupant is Attending to invitation"
|
||||
)
|
||||
confirmed_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=True, comment="Confirmation Date"
|
||||
)
|
||||
token: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Invitation Token"
|
||||
)
|
||||
|
||||
vicarious_person_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=True, comment="Vicarious Person ID"
|
||||
)
|
||||
vicarious_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Vicarious Person UUID"
|
||||
)
|
||||
|
||||
invite_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_invitations.id"), nullable=False
|
||||
)
|
||||
invite_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Invite UUID"
|
||||
)
|
||||
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=False
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Decision Book UUID"
|
||||
)
|
||||
build_living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
build_living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Living Space UUID"
|
||||
)
|
||||
person_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=False)
|
||||
# person_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Person UUID")
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_decision_book_person_ndx_01",
|
||||
build_decision_book_id,
|
||||
invite_id,
|
||||
build_living_space_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "People that are attended to building meetings."},
|
||||
)
|
||||
|
||||
def retrieve_all_occupant_types(self):
|
||||
with self.new_session() as db_session:
|
||||
all_decision_book_people = self.filter_all_system(
|
||||
BuildDecisionBookPersonOccupants.invite_id == self.invite_id,
|
||||
db=db_session,
|
||||
)
|
||||
BuildDecisionBookPersonOccupants.pre_query = all_decision_book_people.query
|
||||
return BuildDecisionBookPersonOccupants.filter_all_system(
|
||||
db=db_session
|
||||
).data
|
||||
|
||||
def get_occupant_types(self):
|
||||
with self.new_session() as db_session:
|
||||
if occupants := BuildDecisionBookPersonOccupants.filter_all(
|
||||
BuildDecisionBookPersonOccupants.build_decision_book_person_id
|
||||
== self.id,
|
||||
db=db_session,
|
||||
).data:
|
||||
return occupants
|
||||
return
|
||||
|
||||
def check_occupant_type(self, occupant_type):
|
||||
with self.new_session() as db_session:
|
||||
book_person_occupant_type = BuildDecisionBookPersonOccupants.filter_one(
|
||||
BuildDecisionBookPersonOccupants.build_decision_book_person_id
|
||||
== self.id,
|
||||
BuildDecisionBookPersonOccupants.occupant_type_id == occupant_type.id,
|
||||
BuildDecisionBookPersonOccupants.active == True,
|
||||
BuildDecisionBookPersonOccupants.is_confirmed == True,
|
||||
db=db_session,
|
||||
).data
|
||||
if not book_person_occupant_type:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Occupant Type : {occupant_type.occupant_code} is not found in "
|
||||
f"Decision Book Person UUID {self.uu_id}",
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookPersonOccupants(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_person_occupants"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_decision_book_person_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_person.id"), nullable=False
|
||||
)
|
||||
build_decision_book_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Person UUID"
|
||||
)
|
||||
invite_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_invitations.id"), nullable=True
|
||||
)
|
||||
invite_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Invite UUID"
|
||||
)
|
||||
|
||||
occupant_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("occupant_types.id"), nullable=False
|
||||
)
|
||||
occupant_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Occupant UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_decision_book_person_occupants_ndx_01",
|
||||
build_decision_book_person_id,
|
||||
occupant_type_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Occupant Types of People that are attended to building meetings."},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookItems(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
item_commentary = metine itiraz şerh maddesi için
|
||||
item_order = maddelerin sıralanma numarası
|
||||
item_objection = maddelerin itiraz şerhi Text şeklinde
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_items"
|
||||
__exclude__fields__ = []
|
||||
|
||||
item_order: Mapped[int] = mapped_column(
|
||||
SmallInteger, nullable=False, comment="Order Number of Item"
|
||||
)
|
||||
item_comment: Mapped[str] = mapped_column(
|
||||
Text, nullable=False, comment="Comment Content"
|
||||
)
|
||||
item_objection: Mapped[str] = mapped_column(
|
||||
Text, nullable=True, comment="Objection Content"
|
||||
)
|
||||
info_is_completed: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Info process is Completed"
|
||||
)
|
||||
is_payment_created: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Are payment Records Created"
|
||||
)
|
||||
|
||||
info_type_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
info_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Info Type UUID"
|
||||
)
|
||||
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=False
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book UUID"
|
||||
)
|
||||
item_short_comment: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=True,
|
||||
comment="This field is reserved for use in grouping data or in the pivot heading.",
|
||||
)
|
||||
|
||||
decision_books: Mapped["BuildDecisionBook"] = relationship(
|
||||
"BuildDecisionBook",
|
||||
back_populates="decision_book_items",
|
||||
foreign_keys=[build_decision_book_id],
|
||||
)
|
||||
decision_book_project: Mapped["BuildDecisionBookProjects"] = relationship(
|
||||
"BuildDecisionBookProjects",
|
||||
back_populates="build_decision_book_item",
|
||||
foreign_keys="BuildDecisionBookProjects.build_decision_book_item_id",
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_build_decision_book_item_ndx_01", build_decision_book_id),
|
||||
Index(
|
||||
"_build_decision_book_item_ndx_02",
|
||||
build_decision_book_id,
|
||||
item_order,
|
||||
unique=True,
|
||||
),
|
||||
{
|
||||
"comment": "Decision Book Items that are related to decision taken at building meetings"
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookItemsUnapproved(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session unapproved personnel
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_items_unapproved"
|
||||
__exclude__fields__ = []
|
||||
|
||||
item_objection: Mapped[str] = mapped_column(
|
||||
Text, nullable=False, comment="Objection Content"
|
||||
)
|
||||
item_order: Mapped[int] = mapped_column(
|
||||
SmallInteger, nullable=False, comment="Order Number"
|
||||
)
|
||||
|
||||
decision_book_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_items.id"), nullable=False
|
||||
)
|
||||
decision_book_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Item"
|
||||
)
|
||||
person_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=False)
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Person UUID"
|
||||
)
|
||||
build_decision_book_item: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_items.id"), nullable=False
|
||||
)
|
||||
build_decision_book_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Item UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_build_decision_book_item_unapproved_ndx_01", build_decision_book_item),
|
||||
{
|
||||
"comment": "People that are unapproved partially or completely in decision book items"
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookPayments(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
period_time = to_char(NEW.process_date, 'YYYY-MM');
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_payments"
|
||||
__exclude__fields__ = []
|
||||
__enum_list__ = [("receive_debit", "DebitTypes", "D")]
|
||||
|
||||
payment_plan_time_periods: Mapped[str] = mapped_column(
|
||||
String(10), nullable=False, comment="Payment Plan Time Periods"
|
||||
)
|
||||
process_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Payment Due Date"
|
||||
)
|
||||
payment_amount: Mapped[float] = mapped_column(
|
||||
Numeric(16, 2), nullable=False, comment="Payment Amount"
|
||||
)
|
||||
currency: Mapped[str] = mapped_column(String(8), server_default="TRY")
|
||||
|
||||
payment_types_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
payment_types_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Dues Type UUID"
|
||||
)
|
||||
|
||||
period_time: Mapped[str] = mapped_column(String(12))
|
||||
process_date_y: Mapped[int] = mapped_column(SmallInteger)
|
||||
process_date_m: Mapped[int] = mapped_column(SmallInteger)
|
||||
|
||||
build_decision_book_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_items.id"),
|
||||
nullable=False,
|
||||
comment="Build Decision Book Item ID",
|
||||
)
|
||||
build_decision_book_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Decision Book Item UUID"
|
||||
)
|
||||
# build_decision_book_id: Mapped[int] = mapped_column(
|
||||
# ForeignKey("build_decision_book.id"), nullable=True
|
||||
# )
|
||||
# build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
# String, nullable=True, comment="Decision Book UUID"
|
||||
# )
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=False
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Part UUID"
|
||||
)
|
||||
decision_book_project_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id"),
|
||||
nullable=True,
|
||||
comment="Decision Book Project ID",
|
||||
)
|
||||
decision_book_project_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Project UUID"
|
||||
)
|
||||
account_records_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("account_records.id"), nullable=True
|
||||
)
|
||||
account_records_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Account Record UU ID"
|
||||
)
|
||||
|
||||
# budget_records_id: Mapped[int] = mapped_column(ForeignKey("account_records.id"), nullable=True)
|
||||
# budget_records_uu_id: Mapped[str] = mapped_column(
|
||||
# String, nullable=True, comment="Budget UUID"
|
||||
# )
|
||||
# accounting_id: Mapped[int] = mapped_column(ForeignKey("account_detail.id"), nullable=True)
|
||||
# accounting_uu_id: Mapped[str] = mapped_column(
|
||||
# String, nullable=True, comment="Accounting UUID"
|
||||
# )
|
||||
# receive_debit_id: Mapped[int] = mapped_column(ForeignKey("api_enum_dropdown.id"), nullable=True)
|
||||
# receive_debit_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Debit UUID")
|
||||
|
||||
# accounting: Mapped["AccountDetail"] = relationship(
|
||||
# "AccountDetail",
|
||||
# back_populates="decision_book_payment_detail",
|
||||
# foreign_keys=[accounting_id],
|
||||
# )
|
||||
#
|
||||
# decision_book_master: Mapped["BuildDecisionBookPaymentsMaster"] = relationship(
|
||||
# "BuildDecisionBookPaymentsMaster",
|
||||
# back_populates="decision_book_payment_detail",
|
||||
# foreign_keys=[build_decision_book_payments_master_id],
|
||||
# )
|
||||
# budget_records: Mapped["CompanyBudgetRecords"] = relationship(
|
||||
# "CompanyBudgetRecords",
|
||||
# back_populates="decision_book_payment_detail",
|
||||
# foreign_keys=[budget_records_id],
|
||||
# )
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"build_decision_book_payments_detail_ndx_00",
|
||||
build_decision_book_item_id,
|
||||
build_parts_id,
|
||||
payment_plan_time_periods,
|
||||
process_date,
|
||||
payment_types_id,
|
||||
account_records_id,
|
||||
unique=True,
|
||||
),
|
||||
Index("build_decision_book_payments_detail_ndx_01", account_records_id),
|
||||
{"comment": "Payment Details of Decision Book Payments"},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookLegal(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
lawsuits_type C:Court Mehkeme M: mediator arabulucu
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_legal"
|
||||
__exclude__fields__ = []
|
||||
|
||||
period_start_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Start Date of Legal Period"
|
||||
)
|
||||
lawsuits_decision_number: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Lawsuits Decision Number"
|
||||
)
|
||||
lawsuits_decision_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Lawsuits Decision Date"
|
||||
)
|
||||
|
||||
period_stop_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="2099-12-31 23:59:59"
|
||||
)
|
||||
decision_book_pdf_path: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=True
|
||||
)
|
||||
resp_company_total_wage: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), server_default="0", nullable=True
|
||||
)
|
||||
contact_agreement_path: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=True
|
||||
)
|
||||
contact_agreement_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00", nullable=True
|
||||
)
|
||||
meeting_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00"
|
||||
)
|
||||
lawsuits_type: Mapped[str] = mapped_column(String(1), server_default="C")
|
||||
lawsuits_name: Mapped[str] = mapped_column(String(128))
|
||||
lawsuits_note: Mapped[str] = mapped_column(String(512))
|
||||
lawyer_cost: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
mediator_lawyer_cost: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
other_cost: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
legal_cost: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
approved_cost: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
total_price: Mapped[float] = mapped_column(Numeric(20, 2))
|
||||
|
||||
build_db_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_items.id"), nullable=False
|
||||
)
|
||||
build_db_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Item UUID"
|
||||
)
|
||||
resp_attorney_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=False
|
||||
)
|
||||
resp_attorney_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Attorney UUID"
|
||||
)
|
||||
resp_attorney_company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"))
|
||||
resp_attorney_company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UUID"
|
||||
)
|
||||
mediator_lawyer_person_id: Mapped[int] = mapped_column(ForeignKey("people.id"))
|
||||
mediator_lawyer_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Mediator Lawyer UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_build_decision_book_legal_ndx_00", meeting_date),
|
||||
{
|
||||
"comment": "Legal items related to decision book items recoreded at building meetings"
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookProjects(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
project_type = C:Court Mehkeme M: mediator arabulucu
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_projects"
|
||||
__exclude__fields__ = []
|
||||
|
||||
project_no: Mapped[str] = mapped_column(
|
||||
String(12), nullable=True, comment="Project Number of Decision Book"
|
||||
)
|
||||
project_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Project Name"
|
||||
)
|
||||
project_start_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Project Start Date"
|
||||
)
|
||||
project_stop_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="2099-12-31 23:59:59"
|
||||
)
|
||||
project_type: Mapped[str] = mapped_column(String, server_default="C")
|
||||
project_note: Mapped[str] = mapped_column(Text)
|
||||
|
||||
decision_book_pdf_path: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=True
|
||||
)
|
||||
is_completed: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Project is Completed"
|
||||
)
|
||||
status_code: Mapped[int] = mapped_column(SmallInteger, nullable=True)
|
||||
resp_company_fix_wage: Mapped[float] = mapped_column(
|
||||
Numeric(10, 2), server_default="0"
|
||||
)
|
||||
is_out_sourced: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
meeting_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00", index=True
|
||||
)
|
||||
currency: Mapped[str] = mapped_column(String(8), server_default="TRY")
|
||||
bid_price: Mapped[float] = mapped_column(Numeric(16, 4), server_default="0")
|
||||
approved_price: Mapped[float] = mapped_column(Numeric(16, 4), server_default="0")
|
||||
final_price: Mapped[float] = mapped_column(Numeric(16, 4), server_default="0")
|
||||
|
||||
contact_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("contracts.id"), nullable=True, comment="Contract id"
|
||||
)
|
||||
contact_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Contract UUID"
|
||||
)
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=False
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book UUID"
|
||||
)
|
||||
build_decision_book_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_items.id"), nullable=False
|
||||
)
|
||||
build_decision_book_item_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Item UUID"
|
||||
)
|
||||
project_response_living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"),
|
||||
nullable=True,
|
||||
comment="Project Response Person ID",
|
||||
)
|
||||
project_response_living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Project Response Person UUID"
|
||||
)
|
||||
resp_company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
resp_company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UUID"
|
||||
)
|
||||
|
||||
build_decision_book_item: Mapped["BuildDecisionBookItems"] = relationship(
|
||||
"BuildDecisionBookItems",
|
||||
back_populates="decision_book_project",
|
||||
foreign_keys=[build_decision_book_item_id],
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_build_decision_book_project_ndx_00",
|
||||
project_no,
|
||||
project_start_date,
|
||||
unique=True,
|
||||
),
|
||||
{
|
||||
"comment": "Project related to decision taken at building meetings on book items"
|
||||
},
|
||||
)
|
||||
|
||||
@property
|
||||
def get_project_year(self):
|
||||
return self.decision_book_items.decision_books.period_start_date.year
|
||||
|
||||
@property
|
||||
def get_project_no(self):
|
||||
return f"{self.get_project_year}-{str(self.id)[-4:].zfill(4)}"
|
||||
|
||||
|
||||
class BuildDecisionBookProjectPerson(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_project_person"
|
||||
__exclude__fields__ = []
|
||||
# __enum_list__ = [("management_typecode", "ProjectTeamTypes", "PTT-EMP")]
|
||||
|
||||
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
job_fix_wage: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
|
||||
bid_price: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
|
||||
decision_price: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
|
||||
|
||||
build_decision_book_project_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id"), nullable=False
|
||||
)
|
||||
build_decision_book_project_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Project UUID"
|
||||
)
|
||||
living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Living Space UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
{"comment": "People that are attended to building project meetings."},
|
||||
)
|
||||
|
||||
|
||||
class BuildDecisionBookProjectItems(CrudCollection):
|
||||
"""
|
||||
Builds class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "build_decision_book_project_items"
|
||||
__exclude__fields__ = []
|
||||
|
||||
item_header: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Item Header"
|
||||
)
|
||||
item_comment: Mapped[str] = mapped_column(
|
||||
Text, nullable=False, comment="Item Comment"
|
||||
)
|
||||
attachment_pdf_path: Mapped[str] = mapped_column(
|
||||
String, server_default="", nullable=True, comment="Attachment PDF Path"
|
||||
)
|
||||
item_estimated_cost: Mapped[float] = mapped_column(
|
||||
Numeric(16, 2), server_default="0", comment="Estimated Cost"
|
||||
)
|
||||
item_short_comment: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=True,
|
||||
comment="This field is reserved for use in grouping data or in the pivot heading.",
|
||||
)
|
||||
|
||||
build_decision_book_project_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id"), nullable=False
|
||||
)
|
||||
build_decision_book_project_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Decision Book Project UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
{"comment": "Project Items related to decision taken at building meetings"},
|
||||
)
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
from typing import Any
|
||||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
class RelationshipDutyCompany(CrudCollection):
|
||||
"""
|
||||
CompanyRelationship class based on declarative_base and CrudCollection via session
|
||||
Company -> Sub Company -> Sub-Sub Company
|
||||
|
||||
if owner_id == parent_id: can manipulate data of any record
|
||||
else: Read-Only
|
||||
duty_id = if relationship_type == base An organization / not operational / no responsible person
|
||||
|
||||
relationship = company_id filter -> Action filter(company_id) relationship_type = Organization
|
||||
relationship = company_id filter -> Action filter(company_id) relationship_type = Commercial
|
||||
"""
|
||||
|
||||
__tablename__ = "relationship_duty_company"
|
||||
__exclude__fields__ = []
|
||||
|
||||
owner_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=False
|
||||
) # 1
|
||||
duties_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("duties.id"), nullable=False
|
||||
) # duty -> (n)employee Evyos LTD
|
||||
|
||||
member_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=False
|
||||
) # 2, 3, 4
|
||||
parent_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
) # None
|
||||
|
||||
relationship_type: Mapped[str] = mapped_column(
|
||||
String, nullable=True, server_default="Commercial"
|
||||
) # Commercial, Organization # Bulk
|
||||
child_count: Mapped[int] = mapped_column(Integer) # 0
|
||||
show_only: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
# related_company: Mapped[List["Companies"]] = relationship(
|
||||
# "Companies",
|
||||
# back_populates="related_companies",
|
||||
# foreign_keys=[related_company_id],
|
||||
# )
|
||||
|
||||
@classmethod
|
||||
def match_company_to_company_commercial(cls, data: Any, token):
|
||||
from Schemas import Duties
|
||||
|
||||
with cls.new_session() as db_session:
|
||||
token_duties_id, token_company_id = token.get("duty_id"), token.get(
|
||||
"company_id"
|
||||
)
|
||||
list_match_company_id = []
|
||||
send_duties = Duties.filter_one(
|
||||
Duties.uu_id == data.duty_uu_id, db=db_session
|
||||
)
|
||||
send_user_duties = Duties.filter_one(
|
||||
Duties.duties_id == send_duties.id,
|
||||
Duties.company_id == token_duties_id,
|
||||
db=db_session,
|
||||
)
|
||||
if not send_user_duties:
|
||||
raise Exception(
|
||||
"Send Duty is not found in company. Please check duty uuid and try again."
|
||||
)
|
||||
|
||||
for company_uu_id in list(data.match_company_uu_id):
|
||||
company = Companies.filter_one(
|
||||
Companies.uu_id == company_uu_id, db=db_session
|
||||
)
|
||||
bulk_company = RelationshipDutyCompany.filter_one(
|
||||
RelationshipDutyCompany.owner_id == token_company_id,
|
||||
RelationshipDutyCompany.relationship_type == "Bulk",
|
||||
RelationshipDutyCompany.member_id == company.id,
|
||||
db=db_session,
|
||||
)
|
||||
if not bulk_company:
|
||||
raise Exception(
|
||||
f"Bulk Company is not found in company. "
|
||||
f"Please check company uuid {bulk_company.uu_id} and try again."
|
||||
)
|
||||
list_match_company_id.append(bulk_company)
|
||||
|
||||
for match_company_id in list_match_company_id:
|
||||
RelationshipDutyCompany.find_or_create(
|
||||
owner_id=token_company_id,
|
||||
duties_id=send_user_duties.id,
|
||||
member_id=match_company_id.id,
|
||||
parent_id=match_company_id.parent_id,
|
||||
relationship_type="Commercial",
|
||||
show_only=False,
|
||||
db=db_session,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def match_company_to_company_organization(cls, data: Any, token):
|
||||
from Schemas import Duties
|
||||
|
||||
with cls.new_session() as db_session:
|
||||
token_duties_id, token_company_id = token.get("duty_id"), token.get(
|
||||
"company_id"
|
||||
)
|
||||
list_match_company_id = []
|
||||
send_duties = Duties.filter_one(
|
||||
Duties.uu_id == data.duty_uu_id, db=db_session
|
||||
)
|
||||
send_user_duties = Duties.filter_one(
|
||||
Duties.duties_id == send_duties.id,
|
||||
Duties.company_id == token_duties_id,
|
||||
db=db_session,
|
||||
)
|
||||
if not send_user_duties:
|
||||
raise Exception(
|
||||
"Send Duty is not found in company. Please check duty uuid and try again."
|
||||
)
|
||||
|
||||
for company_uu_id in list(data.match_company_uu_id):
|
||||
company = Companies.filter_one(
|
||||
Companies.uu_id == company_uu_id, db=db_session
|
||||
)
|
||||
bulk_company = RelationshipDutyCompany.filter_one(
|
||||
RelationshipDutyCompany.owner_id == token_company_id,
|
||||
RelationshipDutyCompany.relationship_type == "Bulk",
|
||||
RelationshipDutyCompany.member_id == company.id,
|
||||
db=db_session,
|
||||
)
|
||||
if not bulk_company:
|
||||
raise Exception(
|
||||
f"Bulk Company is not found in company. "
|
||||
f"Please check company uuid {bulk_company.uu_id} and try again."
|
||||
)
|
||||
list_match_company_id.append(bulk_company)
|
||||
|
||||
for match_company_id in list_match_company_id:
|
||||
Duties.init_a_company_default_duties(
|
||||
company_id=match_company_id.id,
|
||||
company_uu_id=str(match_company_id.uu_id),
|
||||
db=db_session,
|
||||
)
|
||||
RelationshipDutyCompany.find_or_create(
|
||||
owner_id=token_company_id,
|
||||
duties_id=send_user_duties.id,
|
||||
member_id=match_company_id.id,
|
||||
parent_id=match_company_id.parent_id,
|
||||
relationship_type="Organization",
|
||||
show_only=False,
|
||||
db=db_session,
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"_company_relationship_ndx_01",
|
||||
duties_id,
|
||||
owner_id,
|
||||
member_id,
|
||||
relationship_type,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Company Relationship Information"},
|
||||
)
|
||||
|
||||
|
||||
class Companies(CrudCollection):
|
||||
"""
|
||||
Company class based on declarative_base and CrudCollection via session
|
||||
formal_name = Government register name by offical
|
||||
public_name = Public registered name by User
|
||||
nick_name = Search by nickname, commercial_type = Tüzel veya birey
|
||||
"""
|
||||
|
||||
__tablename__ = "companies"
|
||||
|
||||
__exclude__fields__ = ["is_blacklist", "is_commercial"]
|
||||
__access_by__ = []
|
||||
__many__table__ = RelationshipDutyCompany
|
||||
|
||||
formal_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Formal Name"
|
||||
)
|
||||
company_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Company Type"
|
||||
)
|
||||
commercial_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Commercial Type"
|
||||
)
|
||||
tax_no: Mapped[str] = mapped_column(
|
||||
String, index=True, unique=True, nullable=False, comment="Tax No"
|
||||
)
|
||||
|
||||
public_name: Mapped[str] = mapped_column(String, comment="Public Name of a company")
|
||||
company_tag: Mapped[str] = mapped_column(String, comment="Company Tag")
|
||||
default_lang_type: Mapped[str] = mapped_column(String, server_default="TR")
|
||||
default_money_type: Mapped[str] = mapped_column(String, server_default="TL")
|
||||
is_commercial: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
is_blacklist: Mapped[bool] = mapped_column(Boolean, server_default="False")
|
||||
parent_id = mapped_column(Integer, nullable=True)
|
||||
workplace_no: Mapped[str] = mapped_column(String, nullable=True)
|
||||
|
||||
official_address_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("addresses.id"), nullable=True
|
||||
)
|
||||
official_address_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Official Address UUID"
|
||||
)
|
||||
top_responsible_company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
top_responsible_company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Top Responsible Company UUID"
|
||||
)
|
||||
|
||||
# buildings: Mapped[List["Build"]] = relationship(
|
||||
# "Build",
|
||||
# back_populates="companies",
|
||||
# foreign_keys="Build.company_id",
|
||||
# )
|
||||
|
||||
__table_args__ = (
|
||||
Index("_company_ndx_01", tax_no, unique=True),
|
||||
Index("_company_ndx_02", formal_name, public_name),
|
||||
{"comment": "Company Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class Departments(CrudCollection):
|
||||
|
||||
__tablename__ = "departments"
|
||||
__exclude__fields__ = []
|
||||
|
||||
parent_department_id = mapped_column(Integer, server_default="0")
|
||||
department_code = mapped_column(
|
||||
String(16), nullable=False, index=True, comment="Department Code"
|
||||
)
|
||||
department_name: Mapped[str] = mapped_column(
|
||||
String(128), nullable=False, comment="Department Name"
|
||||
)
|
||||
department_description: Mapped[str] = mapped_column(String, server_default="")
|
||||
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=False)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Company UUID"
|
||||
)
|
||||
|
||||
__table_args__ = {"comment": "Departments Information"}
|
||||
|
||||
|
||||
class Duty(CrudCollection):
|
||||
|
||||
__tablename__ = "duty"
|
||||
__exclude__fields__ = []
|
||||
|
||||
duty_name: Mapped[str] = mapped_column(
|
||||
String, unique=True, nullable=False, comment="Duty Name"
|
||||
)
|
||||
duty_code: Mapped[str] = mapped_column(String, nullable=False, comment="Duty Code")
|
||||
duty_description: Mapped[str] = mapped_column(String, comment="Duty Description")
|
||||
|
||||
__table_args__ = ({"comment": "Duty Information"},)
|
||||
|
||||
|
||||
class Duties(CrudCollection):
|
||||
|
||||
__tablename__ = "duties"
|
||||
__exclude__fields__ = []
|
||||
|
||||
users_default_duty = mapped_column(
|
||||
ForeignKey("duty.id"), nullable=True, comment="Default Duty for Users"
|
||||
)
|
||||
company_id: Mapped[int] = mapped_column(Integer)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Company UUID"
|
||||
)
|
||||
duties_id: Mapped[int] = mapped_column(ForeignKey("duty.id"), nullable=False)
|
||||
duties_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Duty UUID"
|
||||
)
|
||||
department_id = mapped_column(
|
||||
ForeignKey("departments.id"), nullable=False, comment="Department ID"
|
||||
)
|
||||
department_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Department UUID"
|
||||
)
|
||||
# priority_id: Mapped[int] = mapped_column(ForeignKey("priority.id"), nullable=True)
|
||||
management_duty = mapped_column(
|
||||
Boolean, server_default="0"
|
||||
) # is this a prime Company Duty ???
|
||||
|
||||
__table_args__ = (
|
||||
Index("duty_ndx_00", company_id, duties_id, department_id, unique=True),
|
||||
{"comment": "Duty & Company & Department Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class Staff(CrudCollection):
|
||||
|
||||
__tablename__ = "staff"
|
||||
__exclude__fields__ = []
|
||||
|
||||
staff_description: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Staff Description"
|
||||
)
|
||||
staff_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Staff Name"
|
||||
)
|
||||
staff_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Staff Code"
|
||||
)
|
||||
|
||||
duties_id: Mapped[int] = mapped_column(ForeignKey("duties.id"), nullable=False)
|
||||
duties_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Duty UUID"
|
||||
)
|
||||
|
||||
__table_args__ = ({"comment": "Staff Information"},)
|
||||
|
||||
|
||||
class Employees(CrudCollection):
|
||||
|
||||
__tablename__ = "employees"
|
||||
__exclude__fields__ = []
|
||||
|
||||
staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id"))
|
||||
staff_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Staff UUID"
|
||||
)
|
||||
people_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
people_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="People UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("employees_ndx_00", people_id, staff_id, unique=True),
|
||||
{"comment": "Employee Person Information"},
|
||||
)
|
||||
|
||||
|
||||
class EmployeeHistory(CrudCollection):
|
||||
|
||||
__tablename__ = "employee_history"
|
||||
__exclude__fields__ = []
|
||||
|
||||
staff_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("staff.id"), nullable=False, comment="Staff ID"
|
||||
)
|
||||
staff_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Staff UUID"
|
||||
)
|
||||
people_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=False, comment="People ID"
|
||||
)
|
||||
people_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="People UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_employee_history_ndx_00", people_id, staff_id),
|
||||
{"comment": "Employee History Information"},
|
||||
)
|
||||
|
||||
|
||||
class EmployeesSalaries(CrudCollection):
|
||||
|
||||
__tablename__ = "employee_salaries"
|
||||
__exclude__fields__ = []
|
||||
|
||||
gross_salary: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Gross Salary"
|
||||
)
|
||||
net_salary: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Net Salary"
|
||||
)
|
||||
people_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=False)
|
||||
people_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="People UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_employee_salaries_ndx_00", people_id, "expiry_starts"),
|
||||
{"comment": "Employee Salaries Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,559 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Numeric,
|
||||
SmallInteger,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class Applications(CrudCollection):
|
||||
"""
|
||||
Applications class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "applications"
|
||||
__exclude__fields__ = []
|
||||
|
||||
name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application Name"
|
||||
)
|
||||
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
|
||||
application_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application Code"
|
||||
)
|
||||
application_type: Mapped[str] = mapped_column(String, comment="Application Type")
|
||||
application_for: Mapped[str] = mapped_column(
|
||||
String, server_default="EMP", comment="Application For"
|
||||
)
|
||||
description: Mapped[str] = mapped_column(String, comment="Application Description")
|
||||
|
||||
|
||||
class Events(CrudCollection):
|
||||
"""
|
||||
Events class based on declarative_base and BaseMixin via session
|
||||
If Events2Occupants and Events2Employees are not found for user request, response 401 Unauthorized
|
||||
"""
|
||||
|
||||
__tablename__ = "events"
|
||||
__exclude__fields__ = []
|
||||
|
||||
function_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="function code", unique=True
|
||||
)
|
||||
function_class: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="class name"
|
||||
)
|
||||
|
||||
# name: Mapped[str] = mapped_column(String, nullable=True) # form or page title
|
||||
description: Mapped[str] = mapped_column(String, server_default="")
|
||||
property_description: Mapped[str] = mapped_column(String, server_default="")
|
||||
|
||||
marketing_layer = mapped_column(SmallInteger, server_default="3")
|
||||
cost: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00")
|
||||
unit_price: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00")
|
||||
|
||||
endpoint_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event Type"
|
||||
)
|
||||
endpoint_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("endpoint_restriction.id"), nullable=True
|
||||
)
|
||||
endpoint_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Endpoint UUID"
|
||||
)
|
||||
|
||||
__table_args__ = ({"comment": "Events Information"},)
|
||||
|
||||
|
||||
class Modules(CrudCollection):
|
||||
"""
|
||||
Modules class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "modules"
|
||||
__exclude__fields__ = []
|
||||
|
||||
module_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Module Name"
|
||||
)
|
||||
module_description: Mapped[str] = mapped_column(String, server_default="")
|
||||
module_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Module Code"
|
||||
)
|
||||
module_layer = mapped_column(Integer, nullable=False, comment="Module Layer")
|
||||
is_default_module = mapped_column(Boolean, server_default="0")
|
||||
|
||||
def retrieve_services(self):
|
||||
services = Services.filter_all(Services.module_id == self.id).data
|
||||
if not services:
|
||||
self.raise_http_exception(
|
||||
status_code="HTTP_404_NOT_FOUND",
|
||||
error_case="RECORD_NOT_FOUND",
|
||||
message=f"No services found for this module : {str(self.uu_id)}",
|
||||
data={
|
||||
"module_uu_id": str(self.uu_id),
|
||||
},
|
||||
)
|
||||
return services
|
||||
|
||||
__table_args__ = ({"comment": "Modules Information"},)
|
||||
|
||||
|
||||
class ModulePrice(CrudCollection):
|
||||
"""
|
||||
ModulePrice class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "module_price"
|
||||
__exclude__fields__ = []
|
||||
|
||||
campaign_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Campaign Code"
|
||||
)
|
||||
module_id: Mapped[int] = mapped_column(ForeignKey("modules.id"), nullable=False)
|
||||
module_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Module UUID"
|
||||
)
|
||||
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
|
||||
service_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Service UUID"
|
||||
)
|
||||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
|
||||
event_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event UUID"
|
||||
)
|
||||
is_counted_percentage: Mapped[float] = mapped_column(
|
||||
Numeric(6, 2), server_default="0.00"
|
||||
) # %22
|
||||
discounted_price: Mapped[float] = mapped_column(
|
||||
Numeric(20, 2), server_default="0.00"
|
||||
) # Normal: 78.00 TL
|
||||
calculated_price: Mapped[float] = mapped_column(
|
||||
Numeric(20, 2), server_default="0.00"
|
||||
) # sana düz 75.00 TL yapar
|
||||
|
||||
__table_args__ = ({"comment": "ModulePrice Information"},)
|
||||
|
||||
|
||||
class Services(CrudCollection):
|
||||
"""
|
||||
Services class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "services"
|
||||
__exclude__fields__ = []
|
||||
|
||||
module_id: Mapped[int] = mapped_column(ForeignKey("modules.id"), nullable=False)
|
||||
module_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Module UUID"
|
||||
)
|
||||
service_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Service Name"
|
||||
)
|
||||
service_description: Mapped[str] = mapped_column(String, server_default="")
|
||||
service_code: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Service Code"
|
||||
)
|
||||
related_responsibility: Mapped[str] = mapped_column(String, server_default="")
|
||||
|
||||
@classmethod
|
||||
def retrieve_service_via_occupant_code(cls, occupant_code):
|
||||
from Schemas import OccupantTypes
|
||||
|
||||
with cls.new_session() as db_session:
|
||||
occupant_type = OccupantTypes.filter_by_one(
|
||||
system=True, occupant_code=occupant_code, db=db_session
|
||||
).data
|
||||
if not occupant_type:
|
||||
cls.raise_http_exception(
|
||||
status_code="HTTP_404_NOT_FOUND",
|
||||
error_case="RECORD_NOT_FOUND",
|
||||
message=f"No occupant type found for this code : {occupant_code}",
|
||||
data={
|
||||
"occupant_code": occupant_code,
|
||||
},
|
||||
)
|
||||
return cls.filter_one(
|
||||
cls.related_responsibility == occupant_type.occupant_code, db=db_session
|
||||
).data
|
||||
|
||||
__table_args__ = ({"comment": "Services Information"},)
|
||||
|
||||
|
||||
class Service2Events(CrudCollection):
|
||||
"""
|
||||
Service2Actions class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "services2events"
|
||||
__exclude__fields__ = []
|
||||
|
||||
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
|
||||
service_uu_id = mapped_column(String, nullable=False, comment="Service UUID")
|
||||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
|
||||
event_uu_id = mapped_column(String, nullable=False, comment="Event UUID")
|
||||
|
||||
__table_args__ = ({"comment": "Service2Events Information"},)
|
||||
|
||||
|
||||
class Service2Application(CrudCollection):
|
||||
"""
|
||||
Service2Application class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "services2applications"
|
||||
__exclude__fields__ = []
|
||||
|
||||
application_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("applications.id"), nullable=False
|
||||
)
|
||||
application_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application UUID"
|
||||
)
|
||||
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
|
||||
service_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Service UUID"
|
||||
)
|
||||
application_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application Code"
|
||||
)
|
||||
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
|
||||
|
||||
__table_args__ = {"comment": "Service2Applications Information"}
|
||||
|
||||
|
||||
class Event2OccupantExtra(CrudCollection):
|
||||
|
||||
__tablename__ = "event2occupant_extra"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
build_living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Living Space UUID"
|
||||
)
|
||||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
|
||||
event_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"event2occupant_extra_bind_event_to_occupant",
|
||||
build_living_space_id,
|
||||
event_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Occupant2Event Information"},
|
||||
)
|
||||
|
||||
|
||||
class Event2EmployeeExtra(CrudCollection):
|
||||
"""
|
||||
Employee2Event class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "event2employee_extra"
|
||||
__exclude__fields__ = []
|
||||
|
||||
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
|
||||
employee_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Employee UUID"
|
||||
)
|
||||
|
||||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
|
||||
event_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"event2employee_extra_employee_to_event",
|
||||
employee_id,
|
||||
event_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Employee to Event Information"},
|
||||
)
|
||||
|
||||
|
||||
class Event2Employee(CrudCollection):
|
||||
"""
|
||||
Employee2Event class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "event2employee"
|
||||
__exclude__fields__ = []
|
||||
|
||||
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
|
||||
employee_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Employee UUID"
|
||||
)
|
||||
event_service_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("services.id"), nullable=False
|
||||
)
|
||||
event_service_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event Cluster UUID"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"event2employee_employee_to_event",
|
||||
employee_id,
|
||||
event_service_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Employee to Event Information"},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_event_codes(cls, employee_id: int, db) -> dict[str:str]:
|
||||
cls.set_session(db)
|
||||
Service2Events.set_session(db)
|
||||
Events.set_session(db)
|
||||
Event2EmployeeExtra.set_session(db)
|
||||
employee_events = cls.query.filter(cls.employee_id == employee_id).all()
|
||||
service_ids = list(set([event.event_service_id for event in employee_events]))
|
||||
active_event_ids = Service2Events.query.filter(Service2Events.service_id.in_(service_ids)).all()
|
||||
active_events = Events.query.filter(Events.id.in_([event.event_id for event in active_event_ids])).all()
|
||||
if extra_events := Event2EmployeeExtra.query.filter(Event2EmployeeExtra.employee_id == employee_id).all():
|
||||
events_extra = Events.query.filter(Events.id.in_([event.event_id for event in extra_events]),).all()
|
||||
active_events.extend(events_extra)
|
||||
events_dict = {}
|
||||
for event in active_events:
|
||||
if not event.endpoint_code in events_dict:
|
||||
events_dict[str(event.endpoint_code)] = str(event.function_code)
|
||||
else:
|
||||
ValueError("Duplicate event code found for single endpoint")
|
||||
return events_dict
|
||||
|
||||
|
||||
class Event2Occupant(CrudCollection):
|
||||
"""
|
||||
Occupant2Event class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "event2occupant"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_living_space_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
build_living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Living Space UUID"
|
||||
)
|
||||
event_service_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("services.id"), nullable=False
|
||||
)
|
||||
event_service_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Event Cluster UUID"
|
||||
)
|
||||
# event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
|
||||
# event_uu_id = mapped_column(String, nullable=False, comment="Event UUID")
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"event2occupant_bind_event_to_occupant",
|
||||
build_living_space_id,
|
||||
event_service_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Occupant2Event Information"},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_event_codes(cls, build_living_space_id: int, db) -> dict[str:str]:
|
||||
cls.set_session(db)
|
||||
Service2Events.set_session(db)
|
||||
Events.set_session(db)
|
||||
occupant_events = cls.query.filter(cls.build_living_space_id == build_living_space_id).all()
|
||||
service_ids = list(set([event.event_service_id for event in occupant_events]))
|
||||
active_event_ids = Service2Events.query.filter(Service2Events.service_id.in_(service_ids)).all()
|
||||
active_events = Events.query.filter(Events.id.in_([event.event_id for event in active_event_ids])).all()
|
||||
if extra_events := Event2OccupantExtra.query.filter(Event2OccupantExtra.build_living_space_id == build_living_space_id).all():
|
||||
events_extra = Events.query.filter(Events.id.in_([event.event_id for event in extra_events])).all()
|
||||
active_events.extend(events_extra)
|
||||
events_dict = {}
|
||||
for event in active_events:
|
||||
if not event.endpoint_code in events_dict:
|
||||
events_dict[str(event.endpoint_code)] = str(event.function_code)
|
||||
else:
|
||||
ValueError("Duplicate event code found for single endpoint")
|
||||
return events_dict
|
||||
|
||||
|
||||
class Application2Employee(CrudCollection):
|
||||
"""
|
||||
Application2Employee class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "application2employee"
|
||||
__exclude__fields__ = []
|
||||
|
||||
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
|
||||
employee_uu_id = mapped_column(String, nullable=False, comment="Employee UUID")
|
||||
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
|
||||
service_uu_id = mapped_column(String, nullable=False, comment="Service UUID")
|
||||
|
||||
@classmethod
|
||||
def get_application_codes(cls, employee_id: int, db) -> list[int]:
|
||||
cls.set_session(db)
|
||||
Service2Application.set_session(db)
|
||||
Applications.set_session(db)
|
||||
Application2EmployeeExtra.set_session(db)
|
||||
employee_services = cls.query.filter(cls.employee_id == employee_id).all()
|
||||
service_ids = [service.service_id for service in employee_services]
|
||||
active_applications = Service2Application.query.filter(Service2Application.service_id.in_(service_ids)).all()
|
||||
applications = Applications.query.filter(Applications.id.in_([application.application_id for application in active_applications])).all()
|
||||
if extra_applications := Application2EmployeeExtra.query.filter(Application2EmployeeExtra.employee_id == employee_id).all():
|
||||
applications_extra = Applications.query.filter(Applications.id.in_([application.application_id for application in extra_applications])).all()
|
||||
applications.extend(applications_extra)
|
||||
applications_dict = {}
|
||||
for application in applications:
|
||||
if not application.site_url in applications_dict:
|
||||
applications_dict[str(application.site_url)] = str(application.application_code)
|
||||
else:
|
||||
ValueError("Duplicate application code found for single endpoint")
|
||||
return applications_dict
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"application2employee_employee_to_service",
|
||||
employee_uu_id,
|
||||
service_uu_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Application to Employee Information"},
|
||||
)
|
||||
|
||||
|
||||
class Application2Occupant(CrudCollection):
|
||||
"""
|
||||
Application2Occupant class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "application2occupant"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
build_living_space_uu_id = mapped_column(
|
||||
String, nullable=False, comment="Build Living Space UUID"
|
||||
)
|
||||
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
|
||||
service_uu_id = mapped_column(String, nullable=False, comment="Service UUID")
|
||||
|
||||
@classmethod
|
||||
def get_application_codes(cls, build_living_space_id: int, db) -> list[int]:
|
||||
cls.set_session(db)
|
||||
Service2Application.set_session(db)
|
||||
Applications.set_session(db)
|
||||
Application2OccupantExtra.set_session(db)
|
||||
occupant_services = cls.query.filter(cls.build_living_space_id == build_living_space_id).all()
|
||||
service_ids = [service.service_id for service in occupant_services]
|
||||
active_applications = Service2Application.query.filter(Service2Application.service_id.in_(service_ids)).all()
|
||||
applications = Applications.query.filter(Applications.id.in_([application.application_id for application in active_applications])).all()
|
||||
if extra_applications := Application2OccupantExtra.query.filter(Application2OccupantExtra.build_living_space_id == build_living_space_id).all():
|
||||
applications_extra = Applications.query.filter(Applications.id.in_([application.application_id for application in extra_applications])).all()
|
||||
applications.extend(applications_extra)
|
||||
applications_dict = {}
|
||||
for application in applications:
|
||||
if not application.site_url in applications_dict:
|
||||
applications_dict[str(application.site_url)] = str(application.application_code)
|
||||
else:
|
||||
ValueError("Duplicate application code found for single endpoint")
|
||||
applications.extend(applications_extra)
|
||||
applications_dict = {}
|
||||
for application in applications:
|
||||
if not application.site_url in applications_dict:
|
||||
applications_dict[str(application.site_url)] = str(application.application_code)
|
||||
else:
|
||||
ValueError("Duplicate application code found for single endpoint")
|
||||
return applications_dict
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"application2occupant_occupant_to_service",
|
||||
build_living_space_uu_id,
|
||||
service_uu_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Application to Occupant Information"},
|
||||
)
|
||||
|
||||
|
||||
class Application2EmployeeExtra(CrudCollection):
|
||||
"""
|
||||
Application2EmployeeExtra class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "application2employee_extra"
|
||||
__exclude__fields__ = []
|
||||
|
||||
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
|
||||
employee_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Employee UUID"
|
||||
)
|
||||
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
|
||||
application_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application UUID"
|
||||
)
|
||||
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
|
||||
application_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application Code"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"application_to_employee",
|
||||
employee_id,
|
||||
site_url,
|
||||
application_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Application2Employee Information"},
|
||||
)
|
||||
|
||||
|
||||
class Application2OccupantExtra(CrudCollection):
|
||||
"""
|
||||
Application2OccupantExtra class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "application2occupant_extra"
|
||||
__exclude__fields__ = []
|
||||
|
||||
build_living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=False
|
||||
)
|
||||
build_living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Build Living Space UUID"
|
||||
)
|
||||
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
|
||||
application_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application UUID"
|
||||
)
|
||||
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
|
||||
application_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Application Code"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"application_to_occupant",
|
||||
build_living_space_id,
|
||||
site_url,
|
||||
application_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Application2Occupant Information"},
|
||||
)
|
||||
|
|
@ -0,0 +1,488 @@
|
|||
import arrow
|
||||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
ForeignKey,
|
||||
Index,
|
||||
TIMESTAMP,
|
||||
Text,
|
||||
func,
|
||||
BigInteger,
|
||||
Numeric,
|
||||
or_,
|
||||
and_,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
relationship,
|
||||
)
|
||||
|
||||
|
||||
class UsersTokens(CrudCollection):
|
||||
|
||||
__tablename__ = "users_tokens"
|
||||
__exclude__fields__ = []
|
||||
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False)
|
||||
|
||||
token_type: Mapped[str] = mapped_column(String(16), server_default="RememberMe")
|
||||
token: Mapped[str] = mapped_column(String, server_default="")
|
||||
domain: Mapped[str] = mapped_column(String, server_default="")
|
||||
expires_at: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True),
|
||||
default=str(arrow.now().shift(days=3)),
|
||||
)
|
||||
|
||||
# users = relationship("Users", back_populates="tokens", foreign_keys=[user_id])
|
||||
|
||||
|
||||
class Credentials(CrudCollection):
|
||||
"""
|
||||
Credentials class to store user credentials
|
||||
"""
|
||||
|
||||
__tablename__ = "credentials"
|
||||
__exclude__fields__ = []
|
||||
|
||||
credential_token: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Credential token for authentication"
|
||||
)
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("users.id"), nullable=False, comment="Foreign key to users table"
|
||||
)
|
||||
user_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="User UUID", index=True
|
||||
)
|
||||
person_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=False, comment="Foreign key to person table"
|
||||
)
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Person UUID", index=True
|
||||
)
|
||||
name: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Name of the user", index=True
|
||||
)
|
||||
surname: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Surname of the user", index=True
|
||||
)
|
||||
email: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Email address of the user", index=True
|
||||
)
|
||||
phone: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Phone number of the user", index=True
|
||||
)
|
||||
is_verified: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Flag to check if user is verified"
|
||||
)
|
||||
|
||||
def generate_token(self) -> str:
|
||||
"""
|
||||
Generate a unique token for the user
|
||||
"""
|
||||
name_token, rest_of_token = "", ""
|
||||
if self.name and self.surname:
|
||||
name_token = f"{self.name[0].upper()}{self.surname[0].upper()}"
|
||||
return ""
|
||||
|
||||
|
||||
class Users(CrudCollection):
|
||||
"""
|
||||
Application User frame to connect to API with assigned token-based HTTP connection
|
||||
"""
|
||||
|
||||
__tablename__ = "users"
|
||||
__exclude__fields__ = [
|
||||
"hash_password",
|
||||
"password_token",
|
||||
"expiry_begins",
|
||||
"related_company",
|
||||
]
|
||||
|
||||
user_tag: Mapped[str] = mapped_column(
|
||||
String(64), server_default="", comment="Unique tag for the user", index=True
|
||||
)
|
||||
email: Mapped[str] = mapped_column(
|
||||
String(128), server_default="", comment="Email address of the user", index=True
|
||||
)
|
||||
phone_number: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Phone number of the user", index=True
|
||||
)
|
||||
via: Mapped[str] = mapped_column(
|
||||
String,
|
||||
server_default="111",
|
||||
comment="Email 1/ Phone 2/ User Tag 3 All 111 Only 100",
|
||||
)
|
||||
|
||||
avatar: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Avatar URL for the user"
|
||||
)
|
||||
hash_password: Mapped[str] = mapped_column(
|
||||
String(256), server_default="", comment="Hashed password for security"
|
||||
)
|
||||
password_token: Mapped[str] = mapped_column(
|
||||
String(256), server_default="", comment="Token for password reset"
|
||||
)
|
||||
remember_me: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0", comment="Flag to remember user login"
|
||||
)
|
||||
|
||||
password_expires_day: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
server_default=str(30),
|
||||
comment="Password expires in days",
|
||||
)
|
||||
password_expiry_begins: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True),
|
||||
server_default=func.now(),
|
||||
comment="Timestamp when password expiry begins",
|
||||
)
|
||||
related_company: Mapped[str] = mapped_column(String, comment="Related Company UUID")
|
||||
|
||||
person_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=False, comment="Foreign key to person table"
|
||||
)
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Person UUID", index=True
|
||||
)
|
||||
local_timezone = mapped_column(
|
||||
String, server_default="GMT+3", comment="Local timezone of user"
|
||||
)
|
||||
person = relationship("People", back_populates="user", foreign_keys=[person_id])
|
||||
default_language: Mapped[str] = mapped_column(
|
||||
String, server_default="tr", comment="Default language of user"
|
||||
)
|
||||
|
||||
|
||||
class RelationshipDutyPeople(CrudCollection):
|
||||
|
||||
__tablename__ = "relationship_duty_people"
|
||||
__exclude__fields__ = []
|
||||
|
||||
company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=False
|
||||
) # 1, 2, 3
|
||||
duties_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("duties.id"), nullable=False
|
||||
) # duty -> (n)person Evyos LTD
|
||||
member_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=False
|
||||
) # 2, 3, 4
|
||||
|
||||
relationship_type: Mapped[str] = mapped_column(
|
||||
String, nullable=True, server_default="Employee"
|
||||
) # Commercial
|
||||
show_only: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
# related_company: Mapped[List["Company"]] = relationship(
|
||||
# "Company",
|
||||
# back_populates="related_companies",
|
||||
# foreign_keys=[related_company_id],
|
||||
# )
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"person_relationship_ndx_01",
|
||||
company_id,
|
||||
duties_id,
|
||||
member_id,
|
||||
relationship_type,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Person Relationship Information"},
|
||||
)
|
||||
|
||||
|
||||
class People(CrudCollection):
|
||||
"""
|
||||
People that are related to users in the application
|
||||
"""
|
||||
|
||||
__tablename__ = "people"
|
||||
__exclude__fields__ = []
|
||||
__many__table__ = RelationshipDutyPeople
|
||||
__encrypt_list__ = [
|
||||
"father_name",
|
||||
"mother_name",
|
||||
"country_code",
|
||||
"national_identity_id",
|
||||
"birth_place",
|
||||
"birth_date",
|
||||
"tax_no",
|
||||
]
|
||||
|
||||
firstname: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="First name of the person"
|
||||
)
|
||||
surname: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, comment="Surname of the person"
|
||||
)
|
||||
middle_name: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Middle name of the person"
|
||||
)
|
||||
sex_code: Mapped[str] = mapped_column(
|
||||
String(1), nullable=False, comment="Sex code of the person (e.g., M/F)"
|
||||
)
|
||||
person_ref: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Reference ID for the person"
|
||||
)
|
||||
person_tag: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Unique tag for the person"
|
||||
)
|
||||
|
||||
# ENCRYPT DATA
|
||||
father_name: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Father's name of the person"
|
||||
)
|
||||
mother_name: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Mother's name of the person"
|
||||
)
|
||||
country_code: Mapped[str] = mapped_column(
|
||||
String(4), server_default="TR", comment="Country code of the person"
|
||||
)
|
||||
national_identity_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="National identity ID of the person"
|
||||
)
|
||||
birth_place: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Birth place of the person"
|
||||
)
|
||||
birth_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True),
|
||||
server_default="1900-01-01",
|
||||
comment="Birth date of the person",
|
||||
)
|
||||
tax_no: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Tax number of the person"
|
||||
)
|
||||
# Receive at Create person
|
||||
# language = mapped_column(
|
||||
# String, comment="Language code of the person"
|
||||
# )
|
||||
# currency = mapped_column(
|
||||
# String, comment="Currency code of the person"
|
||||
# )
|
||||
|
||||
# ENCRYPT DATA
|
||||
user = relationship(
|
||||
"Users", back_populates="person", foreign_keys="Users.person_id"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"person_ndx_001",
|
||||
national_identity_id,
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Person Information"},
|
||||
)
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
if self.middle_name:
|
||||
return f"{self.firstname} {self.middle_name} {self.surname}"
|
||||
return f"{self.firstname} {self.surname}"
|
||||
|
||||
|
||||
class OccupantTypes(CrudCollection):
|
||||
"""
|
||||
Occupant Types class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "occupant_types"
|
||||
__exclude__fields__ = []
|
||||
|
||||
occupant_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Occupant Type"
|
||||
)
|
||||
occupant_description: Mapped[str] = mapped_column(String, server_default="")
|
||||
occupant_code: Mapped[str] = mapped_column(String, server_default="")
|
||||
occupant_category: Mapped[str] = mapped_column(String, server_default="")
|
||||
occupant_category_type: Mapped[str] = mapped_column(String, server_default="")
|
||||
occupant_is_unique: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
__table_args__ = ({"comment": "Occupant Types Information"},)
|
||||
|
||||
|
||||
class Contracts(CrudCollection):
|
||||
"""
|
||||
Contract class based on declarative_base and BaseMixin via session
|
||||
"""
|
||||
|
||||
__tablename__ = "contracts"
|
||||
__exclude__fields__ = []
|
||||
|
||||
contract_type: Mapped[str] = mapped_column(
|
||||
String(5),
|
||||
nullable=False,
|
||||
comment="The code for personnel is P and the code for companies is C.",
|
||||
)
|
||||
contract_title: Mapped[str] = mapped_column(String(255))
|
||||
contract_details: Mapped[str] = mapped_column(Text)
|
||||
contract_terms: Mapped[str] = mapped_column(Text)
|
||||
|
||||
contract_code: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
comment="contract_code is the unique code given by the system.",
|
||||
)
|
||||
contract_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True),
|
||||
server_default="2099-12-31 23:59:59",
|
||||
comment="contract date is the date the contract is made. "
|
||||
"expire start is the start date of the contract, expire en is the end date of the contract.",
|
||||
)
|
||||
|
||||
company_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Company UUID"
|
||||
)
|
||||
|
||||
person_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("people.id"), nullable=True
|
||||
)
|
||||
person_uu_id: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Person UUID"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def retrieve_contact_no(cls):
|
||||
# todo When create record contract_code == below string
|
||||
related_date, counter = Contracts.client_arrow.now(), 1
|
||||
return (
|
||||
f"{related_date.date().year}{str(cls.contract_type)}{str(counter).zfill(6)}"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("_contract_ndx_01", contract_code, unique=True),
|
||||
{"comment": "Contract Information"},
|
||||
)
|
||||
|
||||
|
||||
# @property
|
||||
# def is_occupant(self):
|
||||
# return not str(self.email).split("@")[1] == Auth.ACCESS_EMAIL_EXT
|
||||
#
|
||||
# @property
|
||||
# def is_employee(self):
|
||||
# return str(self.email).split("@")[1] == Auth.ACCESS_EMAIL_EXT
|
||||
#
|
||||
# @property
|
||||
# def user_type(self):
|
||||
# return "Occupant" if self.is_occupant else "Employee"
|
||||
#
|
||||
# @classmethod
|
||||
# def credentials(cls):
|
||||
# db_session = cls.new_session()
|
||||
# person_object: People = People.filter_by_one(
|
||||
# db=db_session, system=True, id=cls.person_id
|
||||
# ).data
|
||||
# if person_object:
|
||||
# return {
|
||||
# "person_id": person_object.id,
|
||||
# "person_uu_id": str(person_object.uu_id),
|
||||
# }
|
||||
# return {
|
||||
# "person_id": None,
|
||||
# "person_uu_id": None,
|
||||
# }
|
||||
#
|
||||
# @property
|
||||
# def password_expiry_ends(self):
|
||||
# """Calculates the expiry end date based on expiry begins and expires day"""
|
||||
# return self.password_expiry_begins + timedelta(
|
||||
# days=int(
|
||||
# "".join(
|
||||
# [
|
||||
# _
|
||||
# for _ in str(self.password_expires_day).split(",")[0]
|
||||
# if _.isdigit()
|
||||
# ]
|
||||
# )
|
||||
# )
|
||||
# )
|
||||
#
|
||||
# @classmethod
|
||||
# def create_action(cls, create_user: InsertUsers, token_dict):
|
||||
# db_session = cls.new_session()
|
||||
# found_person = People.filter_one(
|
||||
# People.uu_id == create_user.people_uu_id,
|
||||
# db=db_session,
|
||||
# ).data
|
||||
#
|
||||
# if not found_person:
|
||||
# raise HTTPException(status_code=400, detail="Person not found.")
|
||||
# if (
|
||||
# not any(i in str(create_user.email) for i in ["@", "."])
|
||||
# and not len(str(create_user.phone_number)) >= 10
|
||||
# ):
|
||||
# raise HTTPException(
|
||||
# status_code=400,
|
||||
# detail="Please enter at least one valid email or phone number.",
|
||||
# )
|
||||
# if not create_user.avatar:
|
||||
# create_user.avatar = ApiStatic.PLACEHOLDER
|
||||
# create_dict = create_user.model_dump()
|
||||
# del create_dict["people_uu_id"]
|
||||
# create_dict["person_id"] = found_person.id
|
||||
# create_dict["person_uu_id"] = str(found_person.uu_id)
|
||||
# create_dict["related_company"] = token_dict.selected_company.company_uu_id
|
||||
# created_user = cls.find_or_create(**create_dict)
|
||||
# created_user.reset_password_token(found_user=created_user)
|
||||
# return created_user
|
||||
#
|
||||
# def get_employee_and_duty_details(self):
|
||||
# from ApiLayers.Schemas import Employees, Duties
|
||||
#
|
||||
# db_session = self.new_session()
|
||||
# found_person = People.filter_one(
|
||||
# People.id == self.person_id,
|
||||
# db=db_session,
|
||||
# )
|
||||
# found_employees = Employees.filter_by_active(
|
||||
# people_id=found_person.id, is_confirmed=True, db=db_session
|
||||
# )
|
||||
# found_duties = Duties.filter_all(
|
||||
# Duties.is_confirmed == True,
|
||||
# Duties.id.in_(
|
||||
# list(found_employee.duty_id for found_employee in found_employees.data)
|
||||
# ),
|
||||
# db=db_session,
|
||||
# )
|
||||
# if not found_employees.count:
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
# detail={
|
||||
# "message": "Person has no confirmed duty. No employee match please register "
|
||||
# "your super admin",
|
||||
# "completed": False,
|
||||
# },
|
||||
# )
|
||||
# return {
|
||||
# "duty_list": [
|
||||
# {
|
||||
# "duty_id": duty.id,
|
||||
# "duty_uu_id": duty.uu_id.__str__(),
|
||||
# "duty_code": duty.duty_code,
|
||||
# "duty_name": duty.duty_name,
|
||||
# "duty_description": duty.duty_description,
|
||||
# }
|
||||
# for duty in found_duties.data
|
||||
# ],
|
||||
# }
|
||||
#
|
||||
# def get_main_domain_and_other_domains(self, get_main_domain: bool = True):
|
||||
# from ApiLayers.Schemas import MongoQueryIdentity
|
||||
#
|
||||
# query_engine = MongoQueryIdentity(company_uuid=self.related_company)
|
||||
# domain_via_user = query_engine.get_domain_via_user(user_uu_id=str(self.uu_id))
|
||||
# if not domain_via_user:
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
# detail="Domain not found. Please contact the admin.",
|
||||
# )
|
||||
# domain_via_user = domain_via_user[0]
|
||||
# if get_main_domain:
|
||||
# return domain_via_user.get("main_domain", None)
|
||||
# return domain_via_user.get("other_domains_list", None)
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
UUID,
|
||||
String,
|
||||
text,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
|
||||
|
||||
class ApiEnumDropdown(CrudCollection):
|
||||
__tablename__ = "api_enum_dropdown"
|
||||
__exclude__fields__ = ["enum_class"]
|
||||
__language_model__ = None
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
uu_id: Mapped[str] = mapped_column(
|
||||
UUID, server_default=text("gen_random_uuid()"), index=True, unique=True
|
||||
)
|
||||
enum_class: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Enum Constant Name"
|
||||
)
|
||||
key: Mapped[str] = mapped_column(String, nullable=False, comment="Enum Key")
|
||||
value: Mapped[str] = mapped_column(String, nullable=False, comment="Enum Value")
|
||||
description: Mapped[str] = mapped_column(String, nullable=True)
|
||||
|
||||
__table_args__ = ({"comment": "Enum objets that are linked to tables"},)
|
||||
|
||||
@classmethod
|
||||
def get_by_uuid(cls, uuid: str):
|
||||
with cls.new_session() as db_session:
|
||||
return cls.filter_by_one(uu_id=str(uuid), db=db_session).data
|
||||
|
||||
@classmethod
|
||||
def get_debit_search(cls, search_debit: str = None, search_uu_id: str = None):
|
||||
with cls.new_session() as db_session:
|
||||
if search_uu_id:
|
||||
if search := cls.filter_one_system(
|
||||
cls.enum_class.in_(["DebitTypes"]),
|
||||
cls.uu_id == search_uu_id,
|
||||
db=db_session,
|
||||
).data:
|
||||
return search
|
||||
elif search_debit:
|
||||
if search := cls.filter_one(
|
||||
cls.enum_class.in_(["DebitTypes"]),
|
||||
cls.key == search_debit,
|
||||
db=db_session,
|
||||
).data:
|
||||
return search
|
||||
return cls.filter_all_system(
|
||||
cls.enum_class.in_(["DebitTypes"]), db=db_session
|
||||
).data
|
||||
|
||||
@classmethod
|
||||
def get_due_types(cls):
|
||||
with cls.new_session() as db_session:
|
||||
if due_list := cls.filter_all_system(
|
||||
cls.enum_class == "BuildDuesTypes",
|
||||
cls.key.in_(["BDT-A", "BDT-D"]),
|
||||
db=db_session,
|
||||
).data:
|
||||
return [due.uu_id.__str__() for due in due_list]
|
||||
# raise HTTPException(
|
||||
# status_code=404,
|
||||
# detail="No dues types found",
|
||||
# )
|
||||
|
||||
@classmethod
|
||||
def due_type_search(cls, search_management: str = None, search_uu_id: str = None):
|
||||
with cls.new_session() as db_session:
|
||||
if search_uu_id:
|
||||
if search := cls.filter_one_system(
|
||||
cls.enum_class.in_(["BuildDuesTypes"]),
|
||||
cls.uu_id == search_uu_id,
|
||||
db=db_session,
|
||||
).data:
|
||||
return search
|
||||
elif search_management:
|
||||
if search := cls.filter_one_system(
|
||||
cls.enum_class.in_(["BuildDuesTypes"]),
|
||||
cls.key == search_management,
|
||||
db=db_session,
|
||||
).data:
|
||||
return search
|
||||
return cls.filter_all_system(
|
||||
cls.enum_class.in_(["BuildDuesTypes"]), db=db_session
|
||||
).data
|
||||
|
||||
def get_enum_dict(self):
|
||||
return {
|
||||
"uu_id": str(self.uu_id),
|
||||
"enum_class": self.enum_class,
|
||||
"key": self.key,
|
||||
"value": self.value,
|
||||
"description": self.description,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def uuid_of_enum(cls, enum_class: str, key: str):
|
||||
with cls.new_session() as db_session:
|
||||
return str(
|
||||
getattr(
|
||||
cls.filter_one_system(
|
||||
cls.enum_class == enum_class, cls.key == key, db=db_session
|
||||
).data,
|
||||
"uu_id",
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from schemas.base_imports import (
|
||||
CrudCollection,
|
||||
UUID,
|
||||
String,
|
||||
mapped_column,
|
||||
Mapped,
|
||||
Index,
|
||||
)
|
||||
|
||||
|
||||
class EndpointRestriction(CrudCollection):
|
||||
"""
|
||||
Initialize Endpoint Restriction with default values
|
||||
"""
|
||||
|
||||
__tablename__ = "endpoint_restriction"
|
||||
__exclude__fields__ = []
|
||||
|
||||
operation_uu_id: Mapped[UUID] = mapped_column(
|
||||
String, comment="UUID of the operation", nullable=False, unique=True
|
||||
)
|
||||
endpoint_function: Mapped[str] = mapped_column(
|
||||
String, comment="Function name of the API endpoint"
|
||||
)
|
||||
endpoint_name: Mapped[str] = mapped_column(
|
||||
String, comment="Name of the API endpoint"
|
||||
)
|
||||
endpoint_method: Mapped[str] = mapped_column(
|
||||
String, comment="HTTP method used by the endpoint"
|
||||
)
|
||||
endpoint_desc: Mapped[str] = mapped_column(
|
||||
String, server_default="", comment="Description of the endpoint"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_endpoint_restriction_operation_uu_id", operation_uu_id, endpoint_method, endpoint_name, unique=True),
|
||||
)
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
MONGO_ENGINE=mongodb
|
||||
MONGO_DB=appdb
|
||||
MONGO_HOST=10.10.2.13
|
||||
MONGO_PORT=27017
|
||||
MONGO_USER=appuser
|
||||
MONGO_AUTH_DB=appdb
|
||||
MONGO_PASSWORD=apppassword
|
||||
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=password
|
||||
POSTGRES_DB=postgres
|
||||
POSTGRES_HOST=10.10.2.14
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_ENGINE=postgresql+psycopg2
|
||||
POSTGRES_POOL_PRE_PING=True
|
||||
POSTGRES_POOL_SIZE=20
|
||||
POSTGRES_MAX_OVERFLOW=10
|
||||
POSTGRES_POOL_RECYCLE=600
|
||||
POSTGRES_POOL_TIMEOUT=30
|
||||
POSTGRES_ECHO=True
|
||||
|
||||
REDIS_HOST=10.10.2.15
|
||||
REDIS_PASSWORD=your_strong_password_here
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB=0
|
||||
|
||||
API_ACCESS_EMAIL_EXT=evyos.com.tr
|
||||
API_ALGORITHM=HS256
|
||||
API_ACCESS_TOKEN_LENGTH=90
|
||||
API_REFRESHER_TOKEN_LENGTH=144
|
||||
API_EMAIL_HOST=10.10.2.36
|
||||
API_DATETIME_FORMAT=YYYY-MM-DD HH:mm:ss Z
|
||||
API_FORGOT_LINK=https://www.evyos.com.tr/password/create?tokenUrl=
|
||||
API_VERSION=0.1.001
|
||||
API_ACCESS_TOKEN_TAG=eys-acs-tkn
|
||||
|
||||
EMAIL_HOST=10.10.2.34
|
||||
EMAIL_USERNAME=karatay@mehmetkaratay.com.tr
|
||||
EMAIL_PASSWORD=system
|
||||
EMAIL_PORT=587
|
||||
EMAIL_SEND=0
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
[project]
|
||||
name = "evyos-services-api"
|
||||
version = "0.1.0"
|
||||
description = "Evyos services api"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"alembic>=1.15.2",
|
||||
"arrow>=1.3.0",
|
||||
"cryptography>=44.0.2",
|
||||
"faker>=37.1.0",
|
||||
"fastapi>=0.115.12",
|
||||
"pandas>=2.2.3",
|
||||
"prometheus-fastapi-instrumentator>=7.1.0",
|
||||
"psycopg2-binary>=2.9.10",
|
||||
"pydantic-settings>=2.8.1",
|
||||
"pymongo>=4.11.3",
|
||||
"pytest>=8.3.5",
|
||||
"redbox>=0.2.1",
|
||||
"redis>=5.2.1",
|
||||
"redmail>=0.6.0",
|
||||
"requests>=2.32.3",
|
||||
"sqlalchemy-mixins>=2.0.5",
|
||||
"textdistance>=4.6.3",
|
||||
"unidecode>=1.3.8",
|
||||
"uvicorn>=0.34.0",
|
||||
]
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Gelen Banka Kayıtları</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Günaydın, Admin</h1>
|
||||
<br />
|
||||
<p>Banka Kayıtları : {{today}}</p>
|
||||
<p><b>Son Bakiye : {{bank_balance}} </b></p>
|
||||
<p>
|
||||
<b
|
||||
>{{"Status : İkinci Bakiye Hatalı" if balance_error else "Status
|
||||
:OK"}}</b
|
||||
>
|
||||
</p>
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for header in headers %}
|
||||
<th>{{ header }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
{% for cell in row %}
|
||||
<td>{{ cell }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Teşekkür ederiz,<br />Evyos Yönetim<br />Saygılarımızla</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# List of all components to install
|
||||
components=(
|
||||
"accordion"
|
||||
"alert"
|
||||
"alert-dialog"
|
||||
"aspect-ratio"
|
||||
"avatar"
|
||||
"badge"
|
||||
"button"
|
||||
"calendar"
|
||||
"card"
|
||||
"checkbox"
|
||||
"collapsible"
|
||||
"combobox"
|
||||
"command"
|
||||
"context-menu"
|
||||
"data-table"
|
||||
"date-picker"
|
||||
"dialog"
|
||||
"dropdown-menu"
|
||||
"form"
|
||||
"hover-card"
|
||||
"input"
|
||||
"label"
|
||||
"menubar"
|
||||
"navigation-menu"
|
||||
"popover"
|
||||
"progress"
|
||||
"radio-group"
|
||||
"scroll-area"
|
||||
"select"
|
||||
"separator"
|
||||
"sheet"
|
||||
"skeleton"
|
||||
"slider"
|
||||
"sonner"
|
||||
"switch"
|
||||
"table"
|
||||
"tabs"
|
||||
"textarea"
|
||||
"toast"
|
||||
"toggle"
|
||||
"tooltip"
|
||||
)
|
||||
|
||||
# Loop and install each component, skipping prompts (to the extent allowed)
|
||||
for component in "${components[@]}"; do
|
||||
echo "Installing $component..."
|
||||
npx --legacy-peer-deps shadcn@latest add "$component" -y || true
|
||||
done
|
||||
|
||||
echo "✅ Next.js with TypeScript and all shadcn components installed successfully!"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "customer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-collapsible": "^1.1.11",
|
||||
"@radix-ui/react-context-menu": "^2.2.15",
|
||||
"@radix-ui/react-dialog": "^1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
||||
"@radix-ui/react-hover-card": "^1.1.14",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-menubar": "^1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.13",
|
||||
"@radix-ui/react-popover": "^1.1.14",
|
||||
"@radix-ui/react-progress": "^1.1.7",
|
||||
"@radix-ui/react-radio-group": "^1.3.7",
|
||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slider": "^1.3.5",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.5",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-toggle": "^1.1.9",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"ioredis": "^5.6.1",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.4",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"zod": "^3.25.36"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@types/node": "^20.17.46",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.2",
|
||||
"tailwindcss": "^4.1.6",
|
||||
"tw-animate-css": "^1.3.2",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,122 @@
|
|||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Accordion({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
||||
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
|
||||
}
|
||||
|
||||
function AccordionItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
||||
return (
|
||||
<AccordionPrimitive.Item
|
||||
data-slot="accordion-item"
|
||||
className={cn("border-b last:border-b-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
||||
return (
|
||||
<AccordionPrimitive.Content
|
||||
data-slot="accordion-content"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
)
|
||||
}
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
function AlertDialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
||||
}
|
||||
|
||||
function AlertDialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
data-slot="alert-dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
||||
return (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogHeader({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogFooter({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogAction({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Action
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogCancel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
className={cn(buttonVariants({ variant: "outline" }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
"use client"
|
||||
|
||||
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
|
||||
|
||||
function AspectRatio({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
|
||||
return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
|
||||
}
|
||||
|
||||
export { AspectRatio }
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn("aspect-square size-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { DayPicker } from "react-day-picker"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayPicker>) {
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn("p-3", className)}
|
||||
classNames={{
|
||||
months: "flex flex-col sm:flex-row gap-2",
|
||||
month: "flex flex-col gap-4",
|
||||
caption: "flex justify-center pt-1 relative items-center w-full",
|
||||
caption_label: "text-sm font-medium",
|
||||
nav: "flex items-center gap-1",
|
||||
nav_button: cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
||||
),
|
||||
nav_button_previous: "absolute left-1",
|
||||
nav_button_next: "absolute right-1",
|
||||
table: "w-full border-collapse space-x-1",
|
||||
head_row: "flex",
|
||||
head_cell:
|
||||
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
|
||||
row: "flex w-full mt-2",
|
||||
cell: cn(
|
||||
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
|
||||
props.mode === "range"
|
||||
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
|
||||
: "[&:has([aria-selected])]:rounded-md"
|
||||
),
|
||||
day: cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"size-8 p-0 font-normal aria-selected:opacity-100"
|
||||
),
|
||||
day_range_start:
|
||||
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
|
||||
day_range_end:
|
||||
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
|
||||
day_selected:
|
||||
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
|
||||
day_today: "bg-accent text-accent-foreground",
|
||||
day_outside:
|
||||
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
|
||||
day_disabled: "text-muted-foreground opacity-50",
|
||||
day_range_middle:
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
||||
day_hidden: "invisible",
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ className, ...props }) => (
|
||||
<ChevronLeft className={cn("size-4", className)} {...props} />
|
||||
),
|
||||
IconRight: ({ className, ...props }) => (
|
||||
<ChevronRight className={cn("size-4", className)} {...props} />
|
||||
),
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Calendar }
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
data-slot="checkbox-indicator"
|
||||
className="flex items-center justify-center text-current transition-none"
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox }
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
"use client"
|
||||
|
||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
||||
|
||||
function Collapsible({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
||||
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
||||
}
|
||||
|
||||
function CollapsibleTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
||||
return (
|
||||
<CollapsiblePrimitive.CollapsibleTrigger
|
||||
data-slot="collapsible-trigger"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CollapsibleContent({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
||||
return (
|
||||
<CollapsiblePrimitive.CollapsibleContent
|
||||
data-slot="collapsible-content"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Command as CommandPrimitive } from "cmdk"
|
||||
import { SearchIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
|
||||
function Command({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive>) {
|
||||
return (
|
||||
<CommandPrimitive
|
||||
data-slot="command"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandDialog({
|
||||
title = "Command Palette",
|
||||
description = "Search for a command to run...",
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Dialog> & {
|
||||
title?: string
|
||||
description?: string
|
||||
}) {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogHeader className="sr-only">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogContent className="overflow-hidden p-0">
|
||||
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
{children}
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandInput({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="command-input-wrapper"
|
||||
className="flex h-9 items-center gap-2 border-b px-3"
|
||||
>
|
||||
<SearchIcon className="size-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.List>) {
|
||||
return (
|
||||
<CommandPrimitive.List
|
||||
data-slot="command-list"
|
||||
className={cn(
|
||||
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandEmpty({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
||||
return (
|
||||
<CommandPrimitive.Empty
|
||||
data-slot="command-empty"
|
||||
className="py-6 text-center text-sm"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandGroup({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
||||
return (
|
||||
<CommandPrimitive.Group
|
||||
data-slot="command-group"
|
||||
className={cn(
|
||||
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
||||
return (
|
||||
<CommandPrimitive.Separator
|
||||
data-slot="command-separator"
|
||||
className={cn("bg-border -mx-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
||||
return (
|
||||
<CommandPrimitive.Item
|
||||
data-slot="command-item"
|
||||
className={cn(
|
||||
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="command-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function ContextMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
|
||||
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
||||
}
|
||||
|
||||
function ContextMenuTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
|
||||
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
|
||||
}
|
||||
|
||||
function ContextMenuRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.RadioGroup
|
||||
data-slot="context-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.SubTrigger
|
||||
data-slot="context-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto" />
|
||||
</ContextMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.SubContent
|
||||
data-slot="context-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Portal>
|
||||
<ContextMenuPrimitive.Content
|
||||
data-slot="context-menu-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</ContextMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuItem({
|
||||
className,
|
||||
inset,
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Item
|
||||
data-slot="context-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.CheckboxItem
|
||||
data-slot="context-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.RadioItem
|
||||
data-slot="context-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Label
|
||||
data-slot="context-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Separator
|
||||
data-slot="context-menu-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="context-menu-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuGroup,
|
||||
ContextMenuPortal,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function DropdownMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
||||
}
|
||||
|
||||
function DropdownMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuContent({
|
||||
className,
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuItem({
|
||||
className,
|
||||
inset,
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
data-slot="dropdown-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Label
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
||||
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
||||
}
|
||||
|
||||
function DropdownMenuSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
useFormState,
|
||||
type ControllerProps,
|
||||
type FieldPath,
|
||||
type FieldValues,
|
||||
} from "react-hook-form"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Label } from "@/components/ui/label"
|
||||
|
||||
const Form = FormProvider
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = {
|
||||
name: TName
|
||||
}
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue
|
||||
)
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext)
|
||||
const itemContext = React.useContext(FormItemContext)
|
||||
const { getFieldState } = useFormContext()
|
||||
const formState = useFormState({ name: fieldContext.name })
|
||||
const fieldState = getFieldState(fieldContext.name, formState)
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>")
|
||||
}
|
||||
|
||||
const { id } = itemContext
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
}
|
||||
}
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string
|
||||
}
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue
|
||||
)
|
||||
|
||||
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
|
||||
const id = React.useId()
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div
|
||||
data-slot="form-item"
|
||||
className={cn("grid gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
</FormItemContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function FormLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
const { error, formItemId } = useFormField()
|
||||
|
||||
return (
|
||||
<Label
|
||||
data-slot="form-label"
|
||||
data-error={!!error}
|
||||
className={cn("data-[error=true]:text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||
|
||||
return (
|
||||
<Slot
|
||||
data-slot="form-control"
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||
const { formDescriptionId } = useFormField()
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-description"
|
||||
id={formDescriptionId}
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
|
||||
const { error, formMessageId } = useFormField()
|
||||
const body = error ? String(error?.message ?? "") : props.children
|
||||
|
||||
if (!body) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-message"
|
||||
id={formMessageId}
|
||||
className={cn("text-destructive text-sm", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function HoverCard({
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
||||
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
|
||||
}
|
||||
|
||||
function HoverCardTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
||||
return (
|
||||
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function HoverCardContent({
|
||||
className,
|
||||
align = "center",
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
||||
return (
|
||||
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
||||
<HoverCardPrimitive.Content
|
||||
data-slot="hover-card-content"
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</HoverCardPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Input }
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Label({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
return (
|
||||
<LabelPrimitive.Root
|
||||
data-slot="label"
|
||||
className={cn(
|
||||
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Label }
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as MenubarPrimitive from "@radix-ui/react-menubar"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Menubar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Root>) {
|
||||
return (
|
||||
<MenubarPrimitive.Root
|
||||
data-slot="menubar"
|
||||
className={cn(
|
||||
"bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
|
||||
return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
|
||||
}
|
||||
|
||||
function MenubarGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Group>) {
|
||||
return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />
|
||||
}
|
||||
|
||||
function MenubarPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
|
||||
return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />
|
||||
}
|
||||
|
||||
function MenubarRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
|
||||
return (
|
||||
<MenubarPrimitive.Trigger
|
||||
data-slot="menubar-trigger"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarContent({
|
||||
className,
|
||||
align = "start",
|
||||
alignOffset = -4,
|
||||
sideOffset = 8,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Content>) {
|
||||
return (
|
||||
<MenubarPortal>
|
||||
<MenubarPrimitive.Content
|
||||
data-slot="menubar-content"
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</MenubarPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarItem({
|
||||
className,
|
||||
inset,
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
return (
|
||||
<MenubarPrimitive.Item
|
||||
data-slot="menubar-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<MenubarPrimitive.CheckboxItem
|
||||
data-slot="menubar-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<MenubarPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</MenubarPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</MenubarPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
|
||||
return (
|
||||
<MenubarPrimitive.RadioItem
|
||||
data-slot="menubar-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<MenubarPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</MenubarPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</MenubarPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<MenubarPrimitive.Label
|
||||
data-slot="menubar-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
|
||||
return (
|
||||
<MenubarPrimitive.Separator
|
||||
data-slot="menubar-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="menubar-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
|
||||
return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
|
||||
}
|
||||
|
||||
function MenubarSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<MenubarPrimitive.SubTrigger
|
||||
data-slot="menubar-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto h-4 w-4" />
|
||||
</MenubarPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function MenubarSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
|
||||
return (
|
||||
<MenubarPrimitive.SubContent
|
||||
data-slot="menubar-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Menubar,
|
||||
MenubarPortal,
|
||||
MenubarMenu,
|
||||
MenubarTrigger,
|
||||
MenubarContent,
|
||||
MenubarGroup,
|
||||
MenubarSeparator,
|
||||
MenubarLabel,
|
||||
MenubarItem,
|
||||
MenubarShortcut,
|
||||
MenubarCheckboxItem,
|
||||
MenubarRadioGroup,
|
||||
MenubarRadioItem,
|
||||
MenubarSub,
|
||||
MenubarSubTrigger,
|
||||
MenubarSubContent,
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function NavigationMenu({
|
||||
className,
|
||||
children,
|
||||
viewport = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
|
||||
viewport?: boolean
|
||||
}) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Root
|
||||
data-slot="navigation-menu"
|
||||
data-viewport={viewport}
|
||||
className={cn(
|
||||
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{viewport && <NavigationMenuViewport />}
|
||||
</NavigationMenuPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.List
|
||||
data-slot="navigation-menu-list"
|
||||
className={cn(
|
||||
"group flex flex-1 list-none items-center justify-center gap-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Item
|
||||
data-slot="navigation-menu-item"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
)
|
||||
|
||||
function NavigationMenuTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
data-slot="navigation-menu-trigger"
|
||||
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDownIcon
|
||||
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Content
|
||||
data-slot="navigation-menu-content"
|
||||
className={cn(
|
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuViewport({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-full left-0 isolate z-50 flex justify-center"
|
||||
)}
|
||||
>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
data-slot="navigation-menu-viewport"
|
||||
className={cn(
|
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuLink({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Link
|
||||
data-slot="navigation-menu-link"
|
||||
className={cn(
|
||||
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuIndicator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
data-slot="navigation-menu-indicator"
|
||||
className={cn(
|
||||
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
navigationMenuTriggerStyle,
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Popover({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
||||
return <PopoverPrimitive.Root data-slot="popover" {...props} />
|
||||
}
|
||||
|
||||
function PopoverTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
|
||||
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
|
||||
}
|
||||
|
||||
function PopoverContent({
|
||||
className,
|
||||
align = "center",
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
||||
return (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
data-slot="popover-content"
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function PopoverAnchor({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
||||
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
|
||||
}
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Progress({
|
||||
className,
|
||||
value,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
className={cn(
|
||||
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="bg-primary h-full w-full flex-1 transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Progress }
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function RadioGroup({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
data-slot="radio-group"
|
||||
className={cn("grid gap-3", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function RadioGroupItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator
|
||||
data-slot="radio-group-indicator"
|
||||
className="relative flex items-center justify-center"
|
||||
>
|
||||
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function ScrollArea({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function ScrollBar({
|
||||
className,
|
||||
orientation = "vertical",
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none p-px transition-colors select-none",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="bg-border relative flex-1 rounded-full"
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Select({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||
return <SelectPrimitive.Root data-slot="select" {...props} />
|
||||
}
|
||||
|
||||
function SelectGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
||||
}
|
||||
|
||||
function SelectValue({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
||||
}
|
||||
|
||||
function SelectTrigger({
|
||||
className,
|
||||
size = "default",
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||
size?: "sm" | "default"
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.Trigger
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDownIcon className="size-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectContent({
|
||||
className,
|
||||
children,
|
||||
position = "popper",
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||
return (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
||||
return (
|
||||
<SelectPrimitive.Label
|
||||
data-slot="select-label"
|
||||
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
||||
return (
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
||||
return (
|
||||
<SelectPrimitive.Separator
|
||||
data-slot="select-separator"
|
||||
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollUpButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollDownButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
orientation = "horizontal",
|
||||
decorative = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
data-slot="separator-root"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Separator }
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||
}
|
||||
|
||||
function SheetTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||
}
|
||||
|
||||
function SheetClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||
}
|
||||
|
||||
function SheetPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||
}
|
||||
|
||||
function SheetOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||
return (
|
||||
<SheetPrimitive.Overlay
|
||||
data-slot="sheet-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetContent({
|
||||
className,
|
||||
children,
|
||||
side = "right",
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||
side?: "top" | "right" | "bottom" | "left"
|
||||
}) {
|
||||
return (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
data-slot="sheet-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
side === "right" &&
|
||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||
side === "left" &&
|
||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||
side === "top" &&
|
||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||
side === "bottom" &&
|
||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||
<XIcon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-header"
|
||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-footer"
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||
return (
|
||||
<SheetPrimitive.Title
|
||||
data-slot="sheet-title"
|
||||
className={cn("text-foreground font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||
return (
|
||||
<SheetPrimitive.Description
|
||||
data-slot="sheet-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Slider({
|
||||
className,
|
||||
defaultValue,
|
||||
value,
|
||||
min = 0,
|
||||
max = 100,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
|
||||
const _values = React.useMemo(
|
||||
() =>
|
||||
Array.isArray(value)
|
||||
? value
|
||||
: Array.isArray(defaultValue)
|
||||
? defaultValue
|
||||
: [min, max],
|
||||
[value, defaultValue, min, max]
|
||||
)
|
||||
|
||||
return (
|
||||
<SliderPrimitive.Root
|
||||
data-slot="slider"
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track
|
||||
data-slot="slider-track"
|
||||
className={cn(
|
||||
"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
||||
)}
|
||||
>
|
||||
<SliderPrimitive.Range
|
||||
data-slot="slider-range"
|
||||
className={cn(
|
||||
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
||||
)}
|
||||
/>
|
||||
</SliderPrimitive.Track>
|
||||
{Array.from({ length: _values.length }, (_, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
data-slot="slider-thumb"
|
||||
key={index}
|
||||
className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||
/>
|
||||
))}
|
||||
</SliderPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Slider }
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"use client"
|
||||
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner, ToasterProps } from "sonner"
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
style={
|
||||
{
|
||||
"--normal-bg": "var(--popover)",
|
||||
"--normal-text": "var(--popover-foreground)",
|
||||
"--normal-border": "var(--border)",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toaster }
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Switch({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
||||
return (
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={cn(
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Switch }
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn(
|
||||
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCaption({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"caption">) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Tabs({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
return (
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Textarea }
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-transparent",
|
||||
outline:
|
||||
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-2 min-w-9",
|
||||
sm: "h-8 px-1.5 min-w-8",
|
||||
lg: "h-10 px-2.5 min-w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Toggle({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TogglePrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>) {
|
||||
return (
|
||||
<TogglePrimitive.Root
|
||||
data-slot="toggle"
|
||||
className={cn(toggleVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toggle, toggleVariants }
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function TooltipProvider({
|
||||
delayDuration = 0,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||
return (
|
||||
<TooltipPrimitive.Provider
|
||||
data-slot="tooltip-provider"
|
||||
delayDuration={delayDuration}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function Tooltip({
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function TooltipTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
||||
}
|
||||
|
||||
function TooltipContent({
|
||||
className,
|
||||
sideOffset = 0,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
||||
return (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# List of all components to install
|
||||
components=(
|
||||
"accordion"
|
||||
"alert"
|
||||
"alert-dialog"
|
||||
"aspect-ratio"
|
||||
"avatar"
|
||||
"badge"
|
||||
"button"
|
||||
"calendar"
|
||||
"card"
|
||||
"checkbox"
|
||||
"collapsible"
|
||||
"combobox"
|
||||
"command"
|
||||
"context-menu"
|
||||
"data-table"
|
||||
"date-picker"
|
||||
"dialog"
|
||||
"dropdown-menu"
|
||||
"form"
|
||||
"hover-card"
|
||||
"input"
|
||||
"label"
|
||||
"menubar"
|
||||
"navigation-menu"
|
||||
"popover"
|
||||
"progress"
|
||||
"radio-group"
|
||||
"scroll-area"
|
||||
"select"
|
||||
"separator"
|
||||
"sheet"
|
||||
"skeleton"
|
||||
"slider"
|
||||
"sonner"
|
||||
"switch"
|
||||
"table"
|
||||
"tabs"
|
||||
"textarea"
|
||||
"toast"
|
||||
"toggle"
|
||||
"tooltip"
|
||||
)
|
||||
|
||||
# Loop and install each component, skipping prompts (to the extent allowed)
|
||||
for component in "${components[@]}"; do
|
||||
echo "Installing $component..."
|
||||
npx --legacy-peer-deps shadcn@latest add "$component" -y || true
|
||||
done
|
||||
|
||||
echo "✅ Next.js with TypeScript and all shadcn components installed successfully!"
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# List of all components to install
|
||||
components=(
|
||||
"accordion"
|
||||
"alert"
|
||||
"alert-dialog"
|
||||
"aspect-ratio"
|
||||
"avatar"
|
||||
"badge"
|
||||
"button"
|
||||
"calendar"
|
||||
"card"
|
||||
"checkbox"
|
||||
"collapsible"
|
||||
"combobox"
|
||||
"command"
|
||||
"context-menu"
|
||||
"data-table"
|
||||
"date-picker"
|
||||
"dialog"
|
||||
"dropdown-menu"
|
||||
"form"
|
||||
"hover-card"
|
||||
"input"
|
||||
"label"
|
||||
"menubar"
|
||||
"navigation-menu"
|
||||
"popover"
|
||||
"progress"
|
||||
"radio-group"
|
||||
"scroll-area"
|
||||
"select"
|
||||
"separator"
|
||||
"sheet"
|
||||
"skeleton"
|
||||
"slider"
|
||||
"sonner"
|
||||
"switch"
|
||||
"table"
|
||||
"tabs"
|
||||
"textarea"
|
||||
"toast"
|
||||
"toggle"
|
||||
"tooltip"
|
||||
)
|
||||
|
||||
# Loop and install each component, skipping prompts (to the extent allowed)
|
||||
for component in "${components[@]}"; do
|
||||
echo "Installing $component..."
|
||||
npx --legacy-peer-deps shadcn@latest add "$component" -y || true
|
||||
done
|
||||
|
||||
echo "✅ Next.js with TypeScript and all shadcn components installed successfully!"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "management",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-collapsible": "^1.1.11",
|
||||
"@radix-ui/react-context-menu": "^2.2.15",
|
||||
"@radix-ui/react-dialog": "^1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
||||
"@radix-ui/react-hover-card": "^1.1.14",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-menubar": "^1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.13",
|
||||
"@radix-ui/react-popover": "^1.1.14",
|
||||
"@radix-ui/react-progress": "^1.1.7",
|
||||
"@radix-ui/react-radio-group": "^1.3.7",
|
||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slider": "^1.3.5",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.5",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-toggle": "^1.1.9",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"ioredis": "^5.6.1",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.4",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"zod": "^3.25.36"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@types/node": "^20.17.46",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.2",
|
||||
"tailwindcss": "^4.1.6",
|
||||
"tw-animate-css": "^1.3.2",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,122 @@
|
|||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Accordion({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
||||
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
|
||||
}
|
||||
|
||||
function AccordionItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
||||
return (
|
||||
<AccordionPrimitive.Item
|
||||
data-slot="accordion-item"
|
||||
className={cn("border-b last:border-b-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
||||
return (
|
||||
<AccordionPrimitive.Content
|
||||
data-slot="accordion-content"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
)
|
||||
}
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
function AlertDialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
||||
}
|
||||
|
||||
function AlertDialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
data-slot="alert-dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
||||
return (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogHeader({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogFooter({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogAction({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Action
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogCancel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
className={cn(buttonVariants({ variant: "outline" }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
"use client"
|
||||
|
||||
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
|
||||
|
||||
function AspectRatio({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
|
||||
return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
|
||||
}
|
||||
|
||||
export { AspectRatio }
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn("aspect-square size-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { DayPicker } from "react-day-picker"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayPicker>) {
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn("p-3", className)}
|
||||
classNames={{
|
||||
months: "flex flex-col sm:flex-row gap-2",
|
||||
month: "flex flex-col gap-4",
|
||||
caption: "flex justify-center pt-1 relative items-center w-full",
|
||||
caption_label: "text-sm font-medium",
|
||||
nav: "flex items-center gap-1",
|
||||
nav_button: cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
||||
),
|
||||
nav_button_previous: "absolute left-1",
|
||||
nav_button_next: "absolute right-1",
|
||||
table: "w-full border-collapse space-x-1",
|
||||
head_row: "flex",
|
||||
head_cell:
|
||||
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
|
||||
row: "flex w-full mt-2",
|
||||
cell: cn(
|
||||
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
|
||||
props.mode === "range"
|
||||
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
|
||||
: "[&:has([aria-selected])]:rounded-md"
|
||||
),
|
||||
day: cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"size-8 p-0 font-normal aria-selected:opacity-100"
|
||||
),
|
||||
day_range_start:
|
||||
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
|
||||
day_range_end:
|
||||
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
|
||||
day_selected:
|
||||
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
|
||||
day_today: "bg-accent text-accent-foreground",
|
||||
day_outside:
|
||||
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
|
||||
day_disabled: "text-muted-foreground opacity-50",
|
||||
day_range_middle:
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
||||
day_hidden: "invisible",
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ className, ...props }) => (
|
||||
<ChevronLeft className={cn("size-4", className)} {...props} />
|
||||
),
|
||||
IconRight: ({ className, ...props }) => (
|
||||
<ChevronRight className={cn("size-4", className)} {...props} />
|
||||
),
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Calendar }
|
||||