mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[Plugin] Plugin context (#9439)
* Pass more stuff to window * Expose form functions to plugin context * Breaking: Render plugin component in context tree - Required due to createRoot function - Adds necessary context providers * Fix context * Provide MantineThemeContext * Bundle mantine/core * Hack for useNavigate within ApiForm - Errors out if called within plugin context - Workaround to catch the error * Update build cmd * Define config for building "Library" mode * Update package.json * Add basic index file * Factor out ApiEndpoints * factor out ModelType * Factor out role enums * Further refactoring * More refactoring * Cleanup * Expose apiUrl function * Add instance data to plugin context type def * Tweaks for loading plugin components - LanguageContext must be on the inside * Tweak StylishText * Externalize notifications system * Update lingui config * Add functions for checking plugin interface version * Extract package version at build time * Enhance version checking * Revert variable name change * Public package * Add README.md * adjust packge name * Adjust name to include org * Update project files * Add basic changelog info * Refactoring to expose URL functions * Refactor navigation functions * Update package and README * Improve navigateToLink function * Refactor stylish text - Move into ./lib - Do not require user state * Revert changes - StylishText throws error in plugin - Low priority, can work out later * expose function to refresh page index * Provide RemoteComponent with a method to reload itself * Bump version * Cleanup tests * Prevent duplicate --emptyOutDir arg * Tweak playwright tests * Expose role and permission enums * Fix imports * Updated docs * Fix spelling, typos, etc * Include more package version information * Expose more version context * Cleanup * Probably don't need hooks * Fix links * Docs updates * Fix links
This commit is contained in:
		
							
								
								
									
										235
									
								
								src/frontend/lib/enums/ApiEndpoints.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								src/frontend/lib/enums/ApiEndpoints.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | ||||
| /* | ||||
|  * Enumeration of available API endpoints. | ||||
|  * | ||||
|  * In the cases where endpoints can be accessed with a primary key, | ||||
|  * the primary key should be appended to the endpoint. | ||||
|  * The exception to this is when the endpoint provides an :id parameter. | ||||
|  */ | ||||
|  | ||||
| export enum ApiEndpoints { | ||||
|   api_server_info = '', | ||||
|  | ||||
|   // User API endpoints | ||||
|   user_list = 'user/', | ||||
|   user_me = 'user/me/', | ||||
|   user_profile = 'user/profile/', | ||||
|   user_roles = 'user/roles/', | ||||
|   user_token = 'user/token/', | ||||
|   user_tokens = 'user/tokens/', | ||||
|   user_simple_login = 'email/generate/', | ||||
|  | ||||
|   // User auth endpoints | ||||
|   user_reset = 'auth/v1/auth/password/request', | ||||
|   user_reset_set = 'auth/v1/auth/password/reset', | ||||
|   auth_pwd_change = 'auth/v1/account/password/change', | ||||
|   auth_login = 'auth/v1/auth/login', | ||||
|   auth_login_2fa = 'auth/v1/auth/2fa/authenticate', | ||||
|   auth_session = 'auth/v1/auth/session', | ||||
|   auth_signup = 'auth/v1/auth/signup', | ||||
|   auth_authenticators = 'auth/v1/account/authenticators', | ||||
|   auth_recovery = 'auth/v1/account/authenticators/recovery-codes', | ||||
|   auth_mfa_reauthenticate = 'auth/v1/auth/2fa/reauthenticate', | ||||
|   auth_totp = 'auth/v1/account/authenticators/totp', | ||||
|   auth_reauthenticate = 'auth/v1/auth/reauthenticate', | ||||
|   auth_email = 'auth/v1/account/email', | ||||
|   auth_email_verify = 'auth/v1/auth/email/verify', | ||||
|   auth_providers = 'auth/v1/account/providers', | ||||
|   auth_provider_redirect = 'auth/v1/auth/provider/redirect', | ||||
|   auth_config = 'auth/v1/config', | ||||
|  | ||||
|   // Generic API endpoints | ||||
|   currency_list = 'currency/exchange/', | ||||
|   currency_refresh = 'currency/refresh/', | ||||
|   all_units = 'units/all/', | ||||
|   task_overview = 'background-task/', | ||||
|   task_pending_list = 'background-task/pending/', | ||||
|   task_scheduled_list = 'background-task/scheduled/', | ||||
|   task_failed_list = 'background-task/failed/', | ||||
|   api_search = 'search/', | ||||
|   settings_global_list = 'settings/global/', | ||||
|   settings_user_list = 'settings/user/', | ||||
|   news = 'news/', | ||||
|   global_status = 'generic/status/', | ||||
|   custom_state_list = 'generic/status/custom/', | ||||
|   version = 'version/', | ||||
|   license = 'license/', | ||||
|   group_list = 'user/group/', | ||||
|   owner_list = 'user/owner/', | ||||
|   ruleset_list = 'user/ruleset/', | ||||
|   content_type_list = 'contenttype/', | ||||
|   icons = 'icons/', | ||||
|   selectionlist_list = 'selection/', | ||||
|   selectionlist_detail = 'selection/:id/', | ||||
|  | ||||
|   // Barcode API endpoints | ||||
|   barcode = 'barcode/', | ||||
|   barcode_history = 'barcode/history/', | ||||
|   barcode_link = 'barcode/link/', | ||||
|   barcode_unlink = 'barcode/unlink/', | ||||
|   barcode_generate = 'barcode/generate/', | ||||
|  | ||||
|   // Data output endpoints | ||||
|   data_output = 'data-output/', | ||||
|  | ||||
|   // Data import endpoints | ||||
|   import_session_list = 'importer/session/', | ||||
|   import_session_accept_fields = 'importer/session/:id/accept_fields/', | ||||
|   import_session_accept_rows = 'importer/session/:id/accept_rows/', | ||||
|   import_session_column_mapping_list = 'importer/column-mapping/', | ||||
|   import_session_row_list = 'importer/row/', | ||||
|  | ||||
|   // Notification endpoints | ||||
|   notifications_list = 'notifications/', | ||||
|   notifications_readall = 'notifications/readall/', | ||||
|  | ||||
|   // Build API endpoints | ||||
|   build_order_list = 'build/', | ||||
|   build_order_issue = 'build/:id/issue/', | ||||
|   build_order_cancel = 'build/:id/cancel/', | ||||
|   build_order_hold = 'build/:id/hold/', | ||||
|   build_order_complete = 'build/:id/finish/', | ||||
|   build_output_complete = 'build/:id/complete/', | ||||
|   build_output_create = 'build/:id/create-output/', | ||||
|   build_output_scrap = 'build/:id/scrap-outputs/', | ||||
|   build_output_delete = 'build/:id/delete-outputs/', | ||||
|   build_order_auto_allocate = 'build/:id/auto-allocate/', | ||||
|   build_order_allocate = 'build/:id/allocate/', | ||||
|   build_order_deallocate = 'build/:id/unallocate/', | ||||
|  | ||||
|   build_line_list = 'build/line/', | ||||
|   build_item_list = 'build/item/', | ||||
|  | ||||
|   bom_list = 'bom/', | ||||
|   bom_item_validate = 'bom/:id/validate/', | ||||
|   bom_validate = 'part/:id/bom-validate/', | ||||
|  | ||||
|   // Part API endpoints | ||||
|   part_list = 'part/', | ||||
|   part_parameter_list = 'part/parameter/', | ||||
|   part_parameter_template_list = 'part/parameter/template/', | ||||
|   part_thumbs_list = 'part/thumbs/', | ||||
|   part_pricing = 'part/:id/pricing/', | ||||
|   part_serial_numbers = 'part/:id/serial-numbers/', | ||||
|   part_scheduling = 'part/:id/scheduling/', | ||||
|   part_pricing_internal = 'part/internal-price/', | ||||
|   part_pricing_sale = 'part/sale-price/', | ||||
|   part_stocktake_list = 'part/stocktake/', | ||||
|   part_stocktake_report_list = 'part/stocktake/report/', | ||||
|   part_stocktake_report_generate = 'part/stocktake/report/generate/', | ||||
|   category_list = 'part/category/', | ||||
|   category_tree = 'part/category/tree/', | ||||
|   category_parameter_list = 'part/category/parameters/', | ||||
|   related_part_list = 'part/related/', | ||||
|   part_test_template_list = 'part/test-template/', | ||||
|  | ||||
|   // Company API endpoints | ||||
|   company_list = 'company/', | ||||
|   contact_list = 'company/contact/', | ||||
|   address_list = 'company/address/', | ||||
|   supplier_part_list = 'company/part/', | ||||
|   supplier_part_pricing_list = 'company/price-break/', | ||||
|   manufacturer_part_list = 'company/part/manufacturer/', | ||||
|   manufacturer_part_parameter_list = 'company/part/manufacturer/parameter/', | ||||
|  | ||||
|   // Stock location endpoints | ||||
|   stock_location_list = 'stock/location/', | ||||
|   stock_location_type_list = 'stock/location-type/', | ||||
|   stock_location_tree = 'stock/location/tree/', | ||||
|  | ||||
|   // Stock item API endpoints | ||||
|   stock_item_list = 'stock/', | ||||
|   stock_tracking_list = 'stock/track/', | ||||
|   stock_test_result_list = 'stock/test/', | ||||
|   stock_transfer = 'stock/transfer/', | ||||
|   stock_remove = 'stock/remove/', | ||||
|   stock_add = 'stock/add/', | ||||
|   stock_count = 'stock/count/', | ||||
|   stock_change_status = 'stock/change_status/', | ||||
|   stock_merge = 'stock/merge/', | ||||
|   stock_assign = 'stock/assign/', | ||||
|   stock_status = 'stock/status/', | ||||
|   stock_install = 'stock/:id/install/', | ||||
|   stock_uninstall = 'stock/:id/uninstall/', | ||||
|   stock_serialize = 'stock/:id/serialize/', | ||||
|   stock_return = 'stock/:id/return/', | ||||
|   stock_serial_info = 'stock/:id/serial-numbers/', | ||||
|  | ||||
|   // Generator API endpoints | ||||
|   generate_batch_code = 'generate/batch-code/', | ||||
|   generate_serial_number = 'generate/serial-number/', | ||||
|  | ||||
|   // Order API endpoints | ||||
|   purchase_order_list = 'order/po/', | ||||
|   purchase_order_issue = 'order/po/:id/issue/', | ||||
|   purchase_order_hold = 'order/po/:id/hold/', | ||||
|   purchase_order_cancel = 'order/po/:id/cancel/', | ||||
|   purchase_order_complete = 'order/po/:id/complete/', | ||||
|   purchase_order_line_list = 'order/po-line/', | ||||
|   purchase_order_extra_line_list = 'order/po-extra-line/', | ||||
|   purchase_order_receive = 'order/po/:id/receive/', | ||||
|  | ||||
|   sales_order_list = 'order/so/', | ||||
|   sales_order_issue = 'order/so/:id/issue/', | ||||
|   sales_order_hold = 'order/so/:id/hold/', | ||||
|   sales_order_cancel = 'order/so/:id/cancel/', | ||||
|   sales_order_ship = 'order/so/:id/ship/', | ||||
|   sales_order_complete = 'order/so/:id/complete/', | ||||
|   sales_order_allocate = 'order/so/:id/allocate/', | ||||
|   sales_order_allocate_serials = 'order/so/:id/allocate-serials/', | ||||
|  | ||||
|   sales_order_line_list = 'order/so-line/', | ||||
|   sales_order_extra_line_list = 'order/so-extra-line/', | ||||
|   sales_order_allocation_list = 'order/so-allocation/', | ||||
|  | ||||
|   sales_order_shipment_list = 'order/so/shipment/', | ||||
|   sales_order_shipment_complete = 'order/so/shipment/:id/ship/', | ||||
|  | ||||
|   return_order_list = 'order/ro/', | ||||
|   return_order_issue = 'order/ro/:id/issue/', | ||||
|   return_order_hold = 'order/ro/:id/hold/', | ||||
|   return_order_cancel = 'order/ro/:id/cancel/', | ||||
|   return_order_complete = 'order/ro/:id/complete/', | ||||
|   return_order_receive = 'order/ro/:id/receive/', | ||||
|   return_order_line_list = 'order/ro-line/', | ||||
|   return_order_extra_line_list = 'order/ro-extra-line/', | ||||
|  | ||||
|   // Template API endpoints | ||||
|   label_list = 'label/template/', | ||||
|   label_print = 'label/print/', | ||||
|   report_list = 'report/template/', | ||||
|   report_print = 'report/print/', | ||||
|   report_snippet = 'report/snippet/', | ||||
|   report_asset = 'report/asset/', | ||||
|  | ||||
|   // Plugin API endpoints | ||||
|   plugin_list = 'plugins/', | ||||
|   plugin_setting_list = 'plugins/:plugin/settings/', | ||||
|   plugin_registry_status = 'plugins/status/', | ||||
|   plugin_install = 'plugins/install/', | ||||
|   plugin_reload = 'plugins/reload/', | ||||
|   plugin_activate = 'plugins/:key/activate/', | ||||
|   plugin_uninstall = 'plugins/:key/uninstall/', | ||||
|   plugin_admin = 'plugins/:key/admin/', | ||||
|  | ||||
|   // User interface plugin endpoints | ||||
|   plugin_ui_features_list = 'plugins/ui/features/:feature_type/', | ||||
|  | ||||
|   // Special plugin endpoints | ||||
|   plugin_locate_item = 'locate/', | ||||
|  | ||||
|   // Machine API endpoints | ||||
|   machine_types_list = 'machine/types/', | ||||
|   machine_driver_list = 'machine/drivers/', | ||||
|   machine_registry_status = 'machine/status/', | ||||
|   machine_list = 'machine/', | ||||
|   machine_restart = 'machine/:machine/restart/', | ||||
|   machine_setting_list = 'machine/:machine/settings/', | ||||
|   machine_setting_detail = 'machine/:machine/settings/:config_type/', | ||||
|  | ||||
|   // Miscellaneous API endpoints | ||||
|   attachment_list = 'attachment/', | ||||
|   error_report_list = 'error-report/', | ||||
|   project_code_list = 'project-code/', | ||||
|   custom_unit_list = 'units/', | ||||
|   notes_image_upload = 'notes-image-upload/' | ||||
| } | ||||
							
								
								
									
										279
									
								
								src/frontend/lib/enums/ModelInformation.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								src/frontend/lib/enums/ModelInformation.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| import { t } from '@lingui/core/macro'; | ||||
| import type { InvenTreeIconType } from '../types/Icons'; | ||||
| import { ApiEndpoints } from './ApiEndpoints'; | ||||
| import type { ModelType } from './ModelType'; | ||||
|  | ||||
| export interface ModelInformationInterface { | ||||
|   label: string; | ||||
|   label_multiple: string; | ||||
|   url_overview?: string; | ||||
|   url_detail?: string; | ||||
|   api_endpoint: ApiEndpoints; | ||||
|   admin_url?: string; | ||||
|   icon: keyof InvenTreeIconType; | ||||
| } | ||||
|  | ||||
| export interface TranslatableModelInformationInterface | ||||
|   extends Omit<ModelInformationInterface, 'label' | 'label_multiple'> { | ||||
|   label: () => string; | ||||
|   label_multiple: () => string; | ||||
| } | ||||
|  | ||||
| export type ModelDict = { | ||||
|   [key in keyof typeof ModelType]: TranslatableModelInformationInterface; | ||||
| }; | ||||
|  | ||||
| export const ModelInformationDict: ModelDict = { | ||||
|   part: { | ||||
|     label: () => t`Part`, | ||||
|     label_multiple: () => t`Parts`, | ||||
|     url_overview: '/part/category/index/parts', | ||||
|     url_detail: '/part/:pk/', | ||||
|     api_endpoint: ApiEndpoints.part_list, | ||||
|     admin_url: '/part/part/', | ||||
|     icon: 'part' | ||||
|   }, | ||||
|   partparametertemplate: { | ||||
|     label: () => t`Part Parameter Template`, | ||||
|     label_multiple: () => t`Part Parameter Templates`, | ||||
|     url_detail: '/partparametertemplate/:pk/', | ||||
|     api_endpoint: ApiEndpoints.part_parameter_template_list, | ||||
|     icon: 'test_templates' | ||||
|   }, | ||||
|   parttesttemplate: { | ||||
|     label: () => t`Part Test Template`, | ||||
|     label_multiple: () => t`Part Test Templates`, | ||||
|     url_detail: '/parttesttemplate/:pk/', | ||||
|     api_endpoint: ApiEndpoints.part_test_template_list, | ||||
|     icon: 'test' | ||||
|   }, | ||||
|   supplierpart: { | ||||
|     label: () => t`Supplier Part`, | ||||
|     label_multiple: () => t`Supplier Parts`, | ||||
|     url_overview: '/purchasing/index/supplier-parts', | ||||
|     url_detail: '/purchasing/supplier-part/:pk/', | ||||
|     api_endpoint: ApiEndpoints.supplier_part_list, | ||||
|     admin_url: '/company/supplierpart/', | ||||
|     icon: 'supplier_part' | ||||
|   }, | ||||
|   manufacturerpart: { | ||||
|     label: () => t`Manufacturer Part`, | ||||
|     label_multiple: () => t`Manufacturer Parts`, | ||||
|     url_overview: '/purchasing/index/manufacturer-parts', | ||||
|     url_detail: '/purchasing/manufacturer-part/:pk/', | ||||
|     api_endpoint: ApiEndpoints.manufacturer_part_list, | ||||
|     admin_url: '/company/manufacturerpart/', | ||||
|     icon: 'manufacturers' | ||||
|   }, | ||||
|   partcategory: { | ||||
|     label: () => t`Part Category`, | ||||
|     label_multiple: () => t`Part Categories`, | ||||
|     url_overview: '/part/category/parts/subcategories', | ||||
|     url_detail: '/part/category/:pk/', | ||||
|     api_endpoint: ApiEndpoints.category_list, | ||||
|     admin_url: '/part/partcategory/', | ||||
|     icon: 'category' | ||||
|   }, | ||||
|   stockitem: { | ||||
|     label: () => t`Stock Item`, | ||||
|     label_multiple: () => t`Stock Items`, | ||||
|     url_overview: '/stock/location/index/stock-items', | ||||
|     url_detail: '/stock/item/:pk/', | ||||
|     api_endpoint: ApiEndpoints.stock_item_list, | ||||
|     admin_url: '/stock/stockitem/', | ||||
|     icon: 'stock' | ||||
|   }, | ||||
|   stocklocation: { | ||||
|     label: () => t`Stock Location`, | ||||
|     label_multiple: () => t`Stock Locations`, | ||||
|     url_overview: '/stock/location', | ||||
|     url_detail: '/stock/location/:pk/', | ||||
|     api_endpoint: ApiEndpoints.stock_location_list, | ||||
|     admin_url: '/stock/stocklocation/', | ||||
|     icon: 'location' | ||||
|   }, | ||||
|   stocklocationtype: { | ||||
|     label: () => t`Stock Location Type`, | ||||
|     label_multiple: () => t`Stock Location Types`, | ||||
|     api_endpoint: ApiEndpoints.stock_location_type_list, | ||||
|     icon: 'location' | ||||
|   }, | ||||
|   stockhistory: { | ||||
|     label: () => t`Stock History`, | ||||
|     label_multiple: () => t`Stock Histories`, | ||||
|     api_endpoint: ApiEndpoints.stock_tracking_list, | ||||
|     icon: 'history' | ||||
|   }, | ||||
|   build: { | ||||
|     label: () => t`Build`, | ||||
|     label_multiple: () => t`Builds`, | ||||
|     url_overview: '/manufacturing/index/buildorders/', | ||||
|     url_detail: '/manufacturing/build-order/:pk/', | ||||
|     api_endpoint: ApiEndpoints.build_order_list, | ||||
|     admin_url: '/build/build/', | ||||
|     icon: 'build_order' | ||||
|   }, | ||||
|   buildline: { | ||||
|     label: () => t`Build Line`, | ||||
|     label_multiple: () => t`Build Lines`, | ||||
|     url_overview: '/build/line', | ||||
|     url_detail: '/build/line/:pk/', | ||||
|     api_endpoint: ApiEndpoints.build_line_list, | ||||
|     icon: 'build_order' | ||||
|   }, | ||||
|   builditem: { | ||||
|     label: () => t`Build Item`, | ||||
|     label_multiple: () => t`Build Items`, | ||||
|     api_endpoint: ApiEndpoints.build_item_list, | ||||
|     icon: 'build_order' | ||||
|   }, | ||||
|   company: { | ||||
|     label: () => t`Company`, | ||||
|     label_multiple: () => t`Companies`, | ||||
|     url_detail: '/company/:pk/', | ||||
|     api_endpoint: ApiEndpoints.company_list, | ||||
|     admin_url: '/company/company/', | ||||
|     icon: 'building' | ||||
|   }, | ||||
|   projectcode: { | ||||
|     label: () => t`Project Code`, | ||||
|     label_multiple: () => t`Project Codes`, | ||||
|     url_detail: '/project-code/:pk/', | ||||
|     api_endpoint: ApiEndpoints.project_code_list, | ||||
|     icon: 'list_details' | ||||
|   }, | ||||
|   purchaseorder: { | ||||
|     label: () => t`Purchase Order`, | ||||
|     label_multiple: () => t`Purchase Orders`, | ||||
|     url_overview: '/purchasing/index/purchaseorders', | ||||
|     url_detail: '/purchasing/purchase-order/:pk/', | ||||
|     api_endpoint: ApiEndpoints.purchase_order_list, | ||||
|     admin_url: '/order/purchaseorder/', | ||||
|     icon: 'purchase_orders' | ||||
|   }, | ||||
|   purchaseorderlineitem: { | ||||
|     label: () => t`Purchase Order Line`, | ||||
|     label_multiple: () => t`Purchase Order Lines`, | ||||
|     api_endpoint: ApiEndpoints.purchase_order_line_list, | ||||
|     icon: 'purchase_orders' | ||||
|   }, | ||||
|   salesorder: { | ||||
|     label: () => t`Sales Order`, | ||||
|     label_multiple: () => t`Sales Orders`, | ||||
|     url_overview: '/sales/index/salesorders', | ||||
|     url_detail: '/sales/sales-order/:pk/', | ||||
|     api_endpoint: ApiEndpoints.sales_order_list, | ||||
|     admin_url: '/order/salesorder/', | ||||
|     icon: 'sales_orders' | ||||
|   }, | ||||
|   salesordershipment: { | ||||
|     label: () => t`Sales Order Shipment`, | ||||
|     label_multiple: () => t`Sales Order Shipments`, | ||||
|     url_detail: '/sales/shipment/:pk/', | ||||
|     api_endpoint: ApiEndpoints.sales_order_shipment_list, | ||||
|     icon: 'sales_orders' | ||||
|   }, | ||||
|   returnorder: { | ||||
|     label: () => t`Return Order`, | ||||
|     label_multiple: () => t`Return Orders`, | ||||
|     url_overview: '/sales/index/returnorders', | ||||
|     url_detail: '/sales/return-order/:pk/', | ||||
|     api_endpoint: ApiEndpoints.return_order_list, | ||||
|     admin_url: '/order/returnorder/', | ||||
|     icon: 'return_orders' | ||||
|   }, | ||||
|   returnorderlineitem: { | ||||
|     label: () => t`Return Order Line Item`, | ||||
|     label_multiple: () => t`Return Order Line Items`, | ||||
|     api_endpoint: ApiEndpoints.return_order_line_list, | ||||
|     icon: 'return_orders' | ||||
|   }, | ||||
|   address: { | ||||
|     label: () => t`Address`, | ||||
|     label_multiple: () => t`Addresses`, | ||||
|     url_detail: '/address/:pk/', | ||||
|     api_endpoint: ApiEndpoints.address_list, | ||||
|     icon: 'address' | ||||
|   }, | ||||
|   contact: { | ||||
|     label: () => t`Contact`, | ||||
|     label_multiple: () => t`Contacts`, | ||||
|     url_detail: '/contact/:pk/', | ||||
|     api_endpoint: ApiEndpoints.contact_list, | ||||
|     icon: 'group' | ||||
|   }, | ||||
|   owner: { | ||||
|     label: () => t`Owner`, | ||||
|     label_multiple: () => t`Owners`, | ||||
|     url_detail: '/owner/:pk/', | ||||
|     api_endpoint: ApiEndpoints.owner_list, | ||||
|     icon: 'group' | ||||
|   }, | ||||
|   user: { | ||||
|     label: () => t`User`, | ||||
|     label_multiple: () => t`Users`, | ||||
|     url_detail: '/core/user/:pk/', | ||||
|     api_endpoint: ApiEndpoints.user_list, | ||||
|     icon: 'user' | ||||
|   }, | ||||
|   group: { | ||||
|     label: () => t`Group`, | ||||
|     label_multiple: () => t`Groups`, | ||||
|     url_detail: '/core/group/:pk/', | ||||
|     api_endpoint: ApiEndpoints.group_list, | ||||
|     admin_url: '/auth/group/', | ||||
|     icon: 'group' | ||||
|   }, | ||||
|   importsession: { | ||||
|     label: () => t`Import Session`, | ||||
|     label_multiple: () => t`Import Sessions`, | ||||
|     url_overview: '/settings/admin/import', | ||||
|     url_detail: '/import/:pk/', | ||||
|     api_endpoint: ApiEndpoints.import_session_list, | ||||
|     icon: 'import' | ||||
|   }, | ||||
|   labeltemplate: { | ||||
|     label: () => t`Label Template`, | ||||
|     label_multiple: () => t`Label Templates`, | ||||
|     url_overview: '/settings/admin/labels', | ||||
|     url_detail: '/settings/admin/labels/:pk/', | ||||
|     api_endpoint: ApiEndpoints.label_list, | ||||
|     icon: 'labels' | ||||
|   }, | ||||
|   reporttemplate: { | ||||
|     label: () => t`Report Template`, | ||||
|     label_multiple: () => t`Report Templates`, | ||||
|     url_overview: '/settings/admin/reports', | ||||
|     url_detail: '/settings/admin/reports/:pk/', | ||||
|     api_endpoint: ApiEndpoints.report_list, | ||||
|     icon: 'reports' | ||||
|   }, | ||||
|   pluginconfig: { | ||||
|     label: () => t`Plugin Configuration`, | ||||
|     label_multiple: () => t`Plugin Configurations`, | ||||
|     url_overview: '/settings/admin/plugin', | ||||
|     url_detail: '/settings/admin/plugin/:pk/', | ||||
|     api_endpoint: ApiEndpoints.plugin_list, | ||||
|     icon: 'plugin' | ||||
|   }, | ||||
|   contenttype: { | ||||
|     label: () => t`Content Type`, | ||||
|     label_multiple: () => t`Content Types`, | ||||
|     api_endpoint: ApiEndpoints.content_type_list, | ||||
|     icon: 'list_details' | ||||
|   }, | ||||
|   selectionlist: { | ||||
|     label: () => t`Selection List`, | ||||
|     label_multiple: () => t`Selection Lists`, | ||||
|     api_endpoint: ApiEndpoints.selectionlist_list, | ||||
|     icon: 'list_details' | ||||
|   }, | ||||
|   error: { | ||||
|     label: () => t`Error`, | ||||
|     label_multiple: () => t`Errors`, | ||||
|     api_endpoint: ApiEndpoints.error_report_list, | ||||
|     url_overview: '/settings/admin/errors', | ||||
|     url_detail: '/settings/admin/errors/:pk/', | ||||
|     icon: 'exclamation' | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										38
									
								
								src/frontend/lib/enums/ModelType.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/frontend/lib/enums/ModelType.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * Enumeration of available API model types | ||||
|  */ | ||||
| export enum ModelType { | ||||
|   part = 'part', | ||||
|   supplierpart = 'supplierpart', | ||||
|   manufacturerpart = 'manufacturerpart', | ||||
|   partcategory = 'partcategory', | ||||
|   partparametertemplate = 'partparametertemplate', | ||||
|   parttesttemplate = 'parttesttemplate', | ||||
|   projectcode = 'projectcode', | ||||
|   stockitem = 'stockitem', | ||||
|   stocklocation = 'stocklocation', | ||||
|   stocklocationtype = 'stocklocationtype', | ||||
|   stockhistory = 'stockhistory', | ||||
|   build = 'build', | ||||
|   buildline = 'buildline', | ||||
|   builditem = 'builditem', | ||||
|   company = 'company', | ||||
|   purchaseorder = 'purchaseorder', | ||||
|   purchaseorderlineitem = 'purchaseorderlineitem', | ||||
|   salesorder = 'salesorder', | ||||
|   salesordershipment = 'salesordershipment', | ||||
|   returnorder = 'returnorder', | ||||
|   returnorderlineitem = 'returnorderlineitem', | ||||
|   importsession = 'importsession', | ||||
|   address = 'address', | ||||
|   contact = 'contact', | ||||
|   owner = 'owner', | ||||
|   user = 'user', | ||||
|   group = 'group', | ||||
|   reporttemplate = 'reporttemplate', | ||||
|   labeltemplate = 'labeltemplate', | ||||
|   pluginconfig = 'pluginconfig', | ||||
|   contenttype = 'contenttype', | ||||
|   selectionlist = 'selectionlist', | ||||
|   error = 'error' | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/frontend/lib/enums/Roles.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/frontend/lib/enums/Roles.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| import { t } from '@lingui/core/macro'; | ||||
|  | ||||
| /* | ||||
|  * Enumeration of available user role groups | ||||
|  */ | ||||
| export enum UserRoles { | ||||
|   admin = 'admin', | ||||
|   build = 'build', | ||||
|   part = 'part', | ||||
|   part_category = 'part_category', | ||||
|   purchase_order = 'purchase_order', | ||||
|   return_order = 'return_order', | ||||
|   sales_order = 'sales_order', | ||||
|   stock = 'stock', | ||||
|   stock_location = 'stock_location', | ||||
|   stocktake = 'stocktake' | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Enumeration of available user permissions within each role group | ||||
|  */ | ||||
| export enum UserPermissions { | ||||
|   view = 'view', | ||||
|   add = 'add', | ||||
|   change = 'change', | ||||
|   delete = 'delete' | ||||
| } | ||||
|  | ||||
| export function userRoleLabel(role: UserRoles): string { | ||||
|   switch (role) { | ||||
|     case UserRoles.admin: | ||||
|       return t`Admin`; | ||||
|     case UserRoles.build: | ||||
|       return t`Build Orders`; | ||||
|     case UserRoles.part: | ||||
|       return t`Parts`; | ||||
|     case UserRoles.part_category: | ||||
|       return t`Part Categories`; | ||||
|     case UserRoles.purchase_order: | ||||
|       return t`Purchase Orders`; | ||||
|     case UserRoles.return_order: | ||||
|       return t`Return Orders`; | ||||
|     case UserRoles.sales_order: | ||||
|       return t`Sales Orders`; | ||||
|     case UserRoles.stock: | ||||
|       return t`Stock Items`; | ||||
|     case UserRoles.stock_location: | ||||
|       return t`Stock Location`; | ||||
|     case UserRoles.stocktake: | ||||
|       return t`Stocktake`; | ||||
|     default: | ||||
|       return role as string; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/frontend/lib/functions/Api.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/frontend/lib/functions/Api.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| import type { ApiEndpoints } from '../enums/ApiEndpoints'; | ||||
| import type { PathParams } from '../types/Core'; | ||||
|  | ||||
| /** | ||||
|  * Function to return the API prefix. | ||||
|  * For now it is fixed, but may be configurable in the future. | ||||
|  */ | ||||
| export function apiPrefix(): string { | ||||
|   return '/api/'; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Construct an API URL with an endpoint and (optional) pk value | ||||
|  */ | ||||
| export function apiUrl( | ||||
|   endpoint: ApiEndpoints | string, | ||||
|   pk?: any, | ||||
|   pathParams?: PathParams | ||||
| ): string { | ||||
|   let _url = endpoint; | ||||
|  | ||||
|   // If the URL does not start with a '/', add the API prefix | ||||
|   if (!_url.startsWith('/')) { | ||||
|     _url = apiPrefix() + _url; | ||||
|   } | ||||
|  | ||||
|   if (_url && pk) { | ||||
|     if (_url.indexOf(':id') >= 0) { | ||||
|       _url = _url.replace(':id', `${pk}`); | ||||
|     } else { | ||||
|       _url += `${pk}/`; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (_url && pathParams) { | ||||
|     for (const key in pathParams) { | ||||
|       _url = _url.replace(`:${key}`, `${pathParams[key]}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return _url; | ||||
| } | ||||
							
								
								
									
										6
									
								
								src/frontend/lib/functions/Events.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/frontend/lib/functions/Events.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| // Helper function to cancel event propagation | ||||
| export function cancelEvent(event: any) { | ||||
|   event?.preventDefault(); | ||||
|   event?.stopPropagation(); | ||||
|   event?.nativeEvent?.stopImmediatePropagation(); | ||||
| } | ||||
							
								
								
									
										70
									
								
								src/frontend/lib/functions/Navigation.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/frontend/lib/functions/Navigation.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| import type { NavigateFunction } from 'react-router-dom'; | ||||
| import { ModelInformationDict } from '../enums/ModelInformation'; | ||||
| import type { ModelType } from '../enums/ModelType'; | ||||
| import { cancelEvent } from './Events'; | ||||
|  | ||||
| export const getBaseUrl = (): string => | ||||
|   (window as any).INVENTREE_SETTINGS?.base_url || 'web'; | ||||
|  | ||||
| /** | ||||
|  * Returns the detail view URL for a given model type | ||||
|  */ | ||||
| export function getDetailUrl( | ||||
|   model: ModelType, | ||||
|   pk: number | string, | ||||
|   absolute?: boolean | ||||
| ): string { | ||||
|   const modelInfo = ModelInformationDict[model]; | ||||
|  | ||||
|   if (pk === undefined || pk === null) { | ||||
|     return ''; | ||||
|   } | ||||
|  | ||||
|   if (!!pk && modelInfo && modelInfo.url_detail) { | ||||
|     const url = modelInfo.url_detail.replace(':pk', pk.toString()); | ||||
|     const base = getBaseUrl(); | ||||
|  | ||||
|     if (absolute && base) { | ||||
|       return `/${base}${url}`; | ||||
|     } else { | ||||
|       return url; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   console.error(`No detail URL found for model ${model} <${pk}>`); | ||||
|   return ''; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Navigate to a provided link. | ||||
|  * - If the link is to be opened externally, open it in a new tab. | ||||
|  * - Otherwise, navigate using the provided navigate function. | ||||
|  */ | ||||
| export const navigateToLink = ( | ||||
|   link: string, | ||||
|   navigate: NavigateFunction, | ||||
|   event: any | ||||
| ) => { | ||||
|   cancelEvent(event); | ||||
|  | ||||
|   const base = `/${getBaseUrl()}`; | ||||
|  | ||||
|   if (event?.ctrlKey || event?.shiftKey) { | ||||
|     // Open the link in a new tab | ||||
|     let url = link; | ||||
|     if (link.startsWith('/') && !link.startsWith(base)) { | ||||
|       url = `${base}${link}`; | ||||
|     } | ||||
|     window.open(url, '_blank'); | ||||
|   } else { | ||||
|     // Navigate internally | ||||
|     let url = link; | ||||
|  | ||||
|     if (link.startsWith(base)) { | ||||
|       // Strip the base URL from the link | ||||
|       url = link.replace(base, ''); | ||||
|     } | ||||
|  | ||||
|     navigate(url); | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										30
									
								
								src/frontend/lib/functions/Plugins.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/frontend/lib/functions/Plugins.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import { | ||||
|   INVENTREE_PLUGIN_VERSION, | ||||
|   type InvenTreePluginContext | ||||
| } from '../types/Plugins'; | ||||
|  | ||||
| function extractVersion(version: string) { | ||||
|   // Extract the version number from the string | ||||
|   const [major, minor, patch] = version | ||||
|     .split('.') | ||||
|     .map((v) => Number.parseInt(v, 10)); | ||||
|  | ||||
|   return { major, minor, patch }; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check th e | ||||
|  */ | ||||
| export function checkPluginVersion(context: InvenTreePluginContext) { | ||||
|   const pluginVersion = extractVersion(INVENTREE_PLUGIN_VERSION); | ||||
|   const systemVersion = extractVersion(context.version.inventree); | ||||
|  | ||||
|   const mismatch = `Plugin version mismatch! Expected version ${INVENTREE_PLUGIN_VERSION}, got ${context.version}`; | ||||
|  | ||||
|   // A major version mismatch indicates a potentially breaking change | ||||
|   if (pluginVersion.major !== systemVersion.major) { | ||||
|     console.warn(mismatch); | ||||
|   } else if (INVENTREE_PLUGIN_VERSION != context.version.inventree) { | ||||
|     console.info(mismatch); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/frontend/lib/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/frontend/lib/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Constant value definitions | ||||
| export { | ||||
|   INVENTREE_PLUGIN_VERSION, | ||||
|   INVENTREE_REACT_VERSION, | ||||
|   INVENTREE_REACT_DOM_VERSION, | ||||
|   INVENTREE_MANTINE_VERSION | ||||
| } from './types/Plugins'; | ||||
|  | ||||
| // Common type definitions | ||||
| export { ApiEndpoints } from './enums/ApiEndpoints'; | ||||
| export { ModelType } from './enums/ModelType'; | ||||
| export { UserRoles, UserPermissions } from './enums/Roles'; | ||||
|  | ||||
| export type { InvenTreePluginContext } from './types/Plugins'; | ||||
|  | ||||
| // Common utility functions | ||||
| export { apiUrl } from './functions/Api'; | ||||
| export { | ||||
|   getBaseUrl, | ||||
|   getDetailUrl, | ||||
|   navigateToLink | ||||
| } from './functions/Navigation'; | ||||
| export { checkPluginVersion } from './functions/Plugins'; | ||||
							
								
								
									
										51
									
								
								src/frontend/lib/types/Auth.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/frontend/lib/types/Auth.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| export interface AuthContext { | ||||
|   status: number; | ||||
|   data: { flows: Flow[] }; | ||||
|   meta: { is_authenticated: boolean }; | ||||
| } | ||||
|  | ||||
| export enum FlowEnum { | ||||
|   VerifyEmail = 'verify_email', | ||||
|   Login = 'login', | ||||
|   Signup = 'signup', | ||||
|   ProviderRedirect = 'provider_redirect', | ||||
|   ProviderSignup = 'provider_signup', | ||||
|   ProviderToken = 'provider_token', | ||||
|   MfaAuthenticate = 'mfa_authenticate', | ||||
|   Reauthenticate = 'reauthenticate', | ||||
|   MfaReauthenticate = 'mfa_reauthenticate' | ||||
| } | ||||
|  | ||||
| export interface Flow { | ||||
|   id: FlowEnum; | ||||
|   providers?: string[]; | ||||
|   is_pending?: boolean[]; | ||||
| } | ||||
|  | ||||
| export interface AuthProvider { | ||||
|   id: string; | ||||
|   name: string; | ||||
|   flows: string[]; | ||||
|   client_id: string; | ||||
| } | ||||
|  | ||||
| export interface AuthConfig { | ||||
|   account: { | ||||
|     authentication_method: string; | ||||
|   }; | ||||
|   socialaccount: { providers: AuthProvider[] }; | ||||
|   mfa: { | ||||
|     supported_types: string[]; | ||||
|   }; | ||||
|   usersessions: { | ||||
|     track_activity: boolean; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| // Errors | ||||
| export type ErrorResponse = { | ||||
|   data: any; | ||||
|   status: number; | ||||
|   statusText: string; | ||||
|   message?: string; | ||||
| }; | ||||
							
								
								
									
										13
									
								
								src/frontend/lib/types/Core.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/frontend/lib/types/Core.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import type { MantineSize } from '@mantine/core'; | ||||
|  | ||||
| export type UiSizeType = MantineSize | string | number; | ||||
|  | ||||
| export interface UserTheme { | ||||
|   primaryColor: string; | ||||
|   whiteColor: string; | ||||
|   blackColor: string; | ||||
|   radius: UiSizeType; | ||||
|   loader: string; | ||||
| } | ||||
|  | ||||
| export type PathParams = Record<string, string | number>; | ||||
							
								
								
									
										69
									
								
								src/frontend/lib/types/Filters.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/frontend/lib/types/Filters.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| import type { ModelType } from '../enums/ModelType'; | ||||
|  | ||||
| /** | ||||
|  * Interface for the table filter choice | ||||
|  */ | ||||
| export type TableFilterChoice = { | ||||
|   value: string; | ||||
|   label: string; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Available filter types | ||||
|  * | ||||
|  * boolean: A simple true/false filter | ||||
|  * choice: A filter which allows selection from a list of (supplied) | ||||
|  * date: A filter which allows selection from a date input | ||||
|  * text: A filter which allows raw text input | ||||
|  * api: A filter which fetches its options from an API endpoint | ||||
|  */ | ||||
| export type TableFilterType = 'boolean' | 'choice' | 'date' | 'text' | 'api'; | ||||
|  | ||||
| /** | ||||
|  * Interface for the table filter type. Provides a number of options for selecting filter value: | ||||
|  * | ||||
|  * name: The name of the filter (used for query string) | ||||
|  * label: The label to display in the UI (human readable) | ||||
|  * description: A description of the filter (human readable) | ||||
|  * type: The type of filter (see TableFilterType) | ||||
|  * choices: A list of TableFilterChoice objects | ||||
|  * choiceFunction: A function which returns a list of TableFilterChoice objects | ||||
|  * defaultValue: The default value for the filter | ||||
|  * value: The current value of the filter | ||||
|  * displayValue: The current display value of the filter | ||||
|  * active: Whether the filter is active (false = hidden, not used) | ||||
|  * apiUrl: The API URL to use for fetching dynamic filter options | ||||
|  * model: The model type to use for fetching dynamic filter options | ||||
|  * modelRenderer: A function to render a simple text version of the model type | ||||
|  */ | ||||
| export type TableFilter = { | ||||
|   name: string; | ||||
|   label: string; | ||||
|   description?: string; | ||||
|   type?: TableFilterType; | ||||
|   choices?: TableFilterChoice[]; | ||||
|   choiceFunction?: () => TableFilterChoice[]; | ||||
|   defaultValue?: any; | ||||
|   value?: any; | ||||
|   displayValue?: any; | ||||
|   active?: boolean; | ||||
|   apiUrl?: string; | ||||
|   model?: ModelType; | ||||
|   modelRenderer?: (instance: any) => string; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Type definition for representing the state of a group of filters. | ||||
|  * These may be applied to a data view (e.g. table, calendar) to filter the displayed data. | ||||
|  * | ||||
|  * filterKey: A unique key for the filter set | ||||
|  * activeFilters: An array of active filters | ||||
|  * setActiveFilters: A function to set the active filters | ||||
|  * clearActiveFilters: A function to clear all active filters | ||||
|  */ | ||||
| export type FilterSetState = { | ||||
|   filterKey: string; | ||||
|   activeFilters: TableFilter[]; | ||||
|   setActiveFilters: (filters: TableFilter[]) => void; | ||||
|   clearActiveFilters: () => void; | ||||
| }; | ||||
							
								
								
									
										187
									
								
								src/frontend/lib/types/Forms.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/frontend/lib/types/Forms.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| import type { DefaultMantineColor, MantineStyleProp } from '@mantine/core'; | ||||
| import type { UseFormReturnType } from '@mantine/form'; | ||||
| import type { ReactNode } from 'react'; | ||||
| import type { FieldValues, UseFormReturn } from 'react-hook-form'; | ||||
| import type { ApiEndpoints } from '../enums/ApiEndpoints'; | ||||
| import type { ModelType } from '../enums/ModelType'; | ||||
| import type { PathParams, UiSizeType } from './Core'; | ||||
| import type { TableState } from './Tables'; | ||||
|  | ||||
| export interface ApiFormAction { | ||||
|   text: string; | ||||
|   variant?: 'outline'; | ||||
|   color?: DefaultMantineColor; | ||||
|   onClick: () => void; | ||||
| } | ||||
|  | ||||
| export type ApiFormData = UseFormReturnType<Record<string, unknown>>; | ||||
|  | ||||
| export type ApiFormAdjustFilterType = { | ||||
|   filters: any; | ||||
|   data: FieldValues; | ||||
| }; | ||||
|  | ||||
| export type ApiFormFieldChoice = { | ||||
|   value: any; | ||||
|   display_name: string; | ||||
| }; | ||||
|  | ||||
| // Define individual headers in a table field | ||||
| export type ApiFormFieldHeader = { | ||||
|   title: string; | ||||
|   style?: MantineStyleProp; | ||||
| }; | ||||
|  | ||||
| /** Definition of the ApiForm field component. | ||||
|  * - The 'name' attribute *must* be provided | ||||
|  * - All other attributes are optional, and may be provided by the API | ||||
|  * - However, they can be overridden by the user | ||||
|  * | ||||
|  * @param name : The name of the field | ||||
|  * @param label : The label to display for the field | ||||
|  * @param value : The value of the field | ||||
|  * @param default : The default value of the field | ||||
|  * @param icon : An icon to display next to the field | ||||
|  * @param field_type : The type of field to render | ||||
|  * @param api_url : The API endpoint to fetch data from (for related fields) | ||||
|  * @param pk_field : The primary key field for the related field (default = "pk") | ||||
|  * @param model : The model to use for related fields | ||||
|  * @param filters : Optional API filters to apply to related fields | ||||
|  * @param required : Whether the field is required | ||||
|  * @param hidden : Whether the field is hidden | ||||
|  * @param disabled : Whether the field is disabled | ||||
|  * @param error : Optional error message to display | ||||
|  * @param exclude : Whether to exclude the field from the submitted data | ||||
|  * @param placeholder : The placeholder text to display | ||||
|  * @param description : The description to display for the field | ||||
|  * @param preFieldContent : Content to render before the field | ||||
|  * @param postFieldContent : Content to render after the field | ||||
|  * @param onValueChange : Callback function to call when the field value changes | ||||
|  * @param adjustFilters : Callback function to adjust the filters for a related field before a query is made | ||||
|  * @param adjustValue : Callback function to adjust the value of the field before it is sent to the API | ||||
|  * @param addRow : Callback function to add a new row to a table field | ||||
|  * @param onKeyDown : Callback function to get which key was pressed in the form to handle submission on enter | ||||
|  */ | ||||
| export type ApiFormFieldType = { | ||||
|   label?: string; | ||||
|   value?: any; | ||||
|   default?: any; | ||||
|   icon?: ReactNode; | ||||
|   field_type?: | ||||
|     | 'related field' | ||||
|     | 'email' | ||||
|     | 'url' | ||||
|     | 'string' | ||||
|     | 'icon' | ||||
|     | 'boolean' | ||||
|     | 'date' | ||||
|     | 'datetime' | ||||
|     | 'integer' | ||||
|     | 'decimal' | ||||
|     | 'float' | ||||
|     | 'number' | ||||
|     | 'choice' | ||||
|     | 'file upload' | ||||
|     | 'nested object' | ||||
|     | 'dependent field' | ||||
|     | 'table'; | ||||
|   api_url?: string; | ||||
|   pk_field?: string; | ||||
|   model?: ModelType; | ||||
|   modelRenderer?: (instance: any) => ReactNode; | ||||
|   filters?: any; | ||||
|   child?: ApiFormFieldType; | ||||
|   children?: { [key: string]: ApiFormFieldType }; | ||||
|   required?: boolean; | ||||
|   error?: string; | ||||
|   choices?: ApiFormFieldChoice[]; | ||||
|   hidden?: boolean; | ||||
|   disabled?: boolean; | ||||
|   exclude?: boolean; | ||||
|   read_only?: boolean; | ||||
|   placeholder?: string; | ||||
|   description?: string; | ||||
|   preFieldContent?: JSX.Element; | ||||
|   postFieldContent?: JSX.Element; | ||||
|   adjustValue?: (value: any) => any; | ||||
|   onValueChange?: (value: any, record?: any) => void; | ||||
|   adjustFilters?: (value: ApiFormAdjustFilterType) => any; | ||||
|   addRow?: () => any; | ||||
|   headers?: ApiFormFieldHeader[]; | ||||
|   depends_on?: string[]; | ||||
| }; | ||||
|  | ||||
| export type ApiFormFieldSet = Record<string, ApiFormFieldType>; | ||||
|  | ||||
| /** | ||||
|  * Properties for the ApiForm component | ||||
|  * @param url : The API endpoint to fetch the form data from | ||||
|  * @param pk : Optional primary-key value when editing an existing object | ||||
|  * @param pk_field : Optional primary-key field name (default: pk) | ||||
|  * @param pathParams : Optional path params for the url | ||||
|  * @param method : Optional HTTP method to use when submitting the form (default: GET) | ||||
|  * @param fields : The fields to render in the form | ||||
|  * @param submitText : Optional custom text to display on the submit button (default: Submit)4 | ||||
|  * @param submitColor : Optional custom color for the submit button (default: green) | ||||
|  * @param fetchInitialData : Optional flag to fetch initial data from the server (default: true) | ||||
|  * @param preFormContent : Optional content to render before the form fields | ||||
|  * @param postFormContent : Optional content to render after the form fields | ||||
|  * @param successMessage : Optional message to display on successful form submission | ||||
|  * @param onFormSuccess : A callback function to call when the form is submitted successfully. | ||||
|  * @param onFormError : A callback function to call when the form is submitted with errors. | ||||
|  * @param processFormData : A callback function to process the form data before submission | ||||
|  * @param checkClose: A callback function to check if the form can be closed after submission | ||||
|  * @param modelType : Define a model type for this form | ||||
|  * @param follow : Boolean, follow the result of the form (if possible) | ||||
|  * @param table : Table to update on success (if provided) | ||||
|  */ | ||||
| export interface ApiFormProps { | ||||
|   url: ApiEndpoints | string; | ||||
|   pk?: number | string; | ||||
|   pk_field?: string; | ||||
|   pathParams?: PathParams; | ||||
|   queryParams?: URLSearchParams; | ||||
|   method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; | ||||
|   fields?: ApiFormFieldSet; | ||||
|   focus?: string; | ||||
|   initialData?: FieldValues; | ||||
|   submitText?: string; | ||||
|   submitColor?: string; | ||||
|   fetchInitialData?: boolean; | ||||
|   ignorePermissionCheck?: boolean; | ||||
|   preFormContent?: JSX.Element; | ||||
|   preFormWarning?: string; | ||||
|   preFormSuccess?: string; | ||||
|   postFormContent?: JSX.Element; | ||||
|   successMessage?: string | null; | ||||
|   onFormSuccess?: (data: any, form: UseFormReturn) => void; | ||||
|   onFormError?: (response: any, form: UseFormReturn) => void; | ||||
|   processFormData?: (data: any, form: UseFormReturn) => any; | ||||
|   checkClose?: (data: any, form: UseFormReturn) => boolean; | ||||
|   table?: TableState; | ||||
|   modelType?: ModelType; | ||||
|   follow?: boolean; | ||||
|   actions?: ApiFormAction[]; | ||||
|   timeout?: number; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @param title : The title to display in the modal header | ||||
|  * @param cancelText : Optional custom text to display on the cancel button (default: Cancel) | ||||
|  * @param cancelColor : Optional custom color for the cancel button (default: blue) | ||||
|  * @param onClose : A callback function to call when the modal is closed. | ||||
|  * @param onOpen : A callback function to call when the modal is opened. | ||||
|  */ | ||||
| export interface ApiFormModalProps extends ApiFormProps { | ||||
|   title: string; | ||||
|   cancelText?: string; | ||||
|   cancelColor?: string; | ||||
|   onClose?: () => void; | ||||
|   onOpen?: () => void; | ||||
|   closeOnClickOutside?: boolean; | ||||
|   size?: UiSizeType; | ||||
| } | ||||
|  | ||||
| export interface BulkEditApiFormModalProps extends ApiFormModalProps { | ||||
|   items: number[]; | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/frontend/lib/types/Icons.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/frontend/lib/types/Icons.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| import type { Icon, IconProps } from '@tabler/icons-react'; | ||||
|  | ||||
| export type TablerIconType = React.ForwardRefExoticComponent< | ||||
|   Omit<IconProps, 'ref'> & React.RefAttributes<Icon> | ||||
| >; | ||||
|  | ||||
| export type InvenTreeIconType = { | ||||
|   [key: string]: TablerIconType; | ||||
| }; | ||||
							
								
								
									
										17
									
								
								src/frontend/lib/types/Modals.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/frontend/lib/types/Modals.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import type { UiSizeType } from './Core'; | ||||
|  | ||||
| export interface UseModalProps { | ||||
|   title: string; | ||||
|   children: React.ReactElement; | ||||
|   size?: UiSizeType; | ||||
|   onOpen?: () => void; | ||||
|   onClose?: () => void; | ||||
|   closeOnClickOutside?: boolean; | ||||
| } | ||||
|  | ||||
| export interface UseModalReturn { | ||||
|   open: () => void; | ||||
|   close: () => void; | ||||
|   toggle: () => void; | ||||
|   modal: React.ReactElement; | ||||
| } | ||||
							
								
								
									
										87
									
								
								src/frontend/lib/types/Plugins.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/frontend/lib/types/Plugins.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| import type { MantineColorScheme, MantineTheme } from '@mantine/core'; | ||||
| import type { QueryClient } from '@tanstack/react-query'; | ||||
| import type { AxiosInstance } from 'axios'; | ||||
| import type { NavigateFunction } from 'react-router-dom'; | ||||
| import type { ModelType } from '../enums/ModelType'; | ||||
| import type { ApiFormModalProps, BulkEditApiFormModalProps } from './Forms'; | ||||
| import type { UseModalReturn } from './Modals'; | ||||
| import type { SettingsStateProps } from './Settings'; | ||||
| import type { UserStateProps } from './User'; | ||||
|  | ||||
| export interface PluginProps { | ||||
|   name: string; | ||||
|   slug: string; | ||||
|   version: null | string; | ||||
| } | ||||
|  | ||||
| export interface PluginVersion { | ||||
|   inventree: string; | ||||
|   react: string; | ||||
|   reactDom: string; | ||||
|   mantine: string; | ||||
| } | ||||
|  | ||||
| export type InvenTreeFormsContext = { | ||||
|   bulkEdit: (props: BulkEditApiFormModalProps) => UseModalReturn; | ||||
|   create: (props: ApiFormModalProps) => UseModalReturn; | ||||
|   delete: (props: ApiFormModalProps) => UseModalReturn; | ||||
|   edit: (props: ApiFormModalProps) => UseModalReturn; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * A set of properties which are passed to a plugin, | ||||
|  * for rendering an element in the user interface. | ||||
|  * | ||||
|  * @param version - The version of the running InvenTree software stack | ||||
|  * @param api - The Axios API instance (see ../states/ApiState.tsx) | ||||
|  * @param user - The current user instance (see ../states/UserState.tsx) | ||||
|  * @param userSettings - The current user settings (see ../states/SettingsState.tsx) | ||||
|  * @param globalSettings - The global settings (see ../states/SettingsState.tsx) | ||||
|  * @param navigate - The navigation function (see react-router-dom) | ||||
|  * @param theme - The current Mantine theme | ||||
|  * @param colorScheme - The current Mantine color scheme (e.g. 'light' / 'dark') | ||||
|  * @param host - The current host URL | ||||
|  * @param locale - The current locale string (e.g. 'en' / 'de') | ||||
|  * @param model - The model type associated with the rendered component (if applicable) | ||||
|  * @param id - The ID (primary key) of the model instance for the plugin (if applicable) | ||||
|  * @param instance - The model instance data (if available) | ||||
|  * @param reloadContent - A function which can be called to reload the plugin content | ||||
|  * @param reloadInstance - A function which can be called to reload the model instance | ||||
|  * @param context - Any additional context data which may be passed to the plugin | ||||
|  */ | ||||
| export type InvenTreePluginContext = { | ||||
|   version: PluginVersion; | ||||
|   api: AxiosInstance; | ||||
|   queryClient: QueryClient; | ||||
|   user: UserStateProps; | ||||
|   userSettings: SettingsStateProps; | ||||
|   globalSettings: SettingsStateProps; | ||||
|   host: string; | ||||
|   locale: string; | ||||
|   navigate: NavigateFunction; | ||||
|   theme: MantineTheme; | ||||
|   forms: InvenTreeFormsContext; | ||||
|   colorScheme: MantineColorScheme; | ||||
|   model?: ModelType | string; | ||||
|   id?: string | number | null; | ||||
|   instance?: any; | ||||
|   reloadContent?: () => void; | ||||
|   reloadInstance?: () => void; | ||||
|   context?: any; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * The version of the InvenTree plugin context interface. | ||||
|  * This number should be incremented if the interface changes. | ||||
|  */ | ||||
|  | ||||
| // @ts-ignore | ||||
| export const INVENTREE_PLUGIN_VERSION: string = __INVENTREE_LIB_VERSION__; | ||||
| // @ts-ignore | ||||
| export const INVENTREE_REACT_VERSION: string = __INVENTREE_REACT_VERSION__; | ||||
| // @ts-ignore | ||||
| export const INVENTREE_REACT_DOM_VERSION: string = | ||||
|   // @ts-ignore | ||||
|   __INVENTREE_REACT_DOM_VERSION__; | ||||
| // @ts-ignore | ||||
| export const INVENTREE_MANTINE_VERSION: string = __INVENTREE_MANTINE_VERSION__; | ||||
							
								
								
									
										8
									
								
								src/frontend/lib/types/Server.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/frontend/lib/types/Server.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| export interface Host { | ||||
|   host: string; | ||||
|   name: string; | ||||
| } | ||||
|  | ||||
| export interface HostList { | ||||
|   [key: string]: Host; | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/frontend/lib/types/Settings.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/frontend/lib/types/Settings.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| import type { ApiEndpoints } from '../enums/ApiEndpoints'; | ||||
| import type { PathParams } from './Core'; | ||||
|  | ||||
| export enum SettingTyp { | ||||
|   InvenTree = 'inventree', | ||||
|   Plugin = 'plugin', | ||||
|   User = 'user', | ||||
|   Notification = 'notification' | ||||
| } | ||||
|  | ||||
| export enum SettingType { | ||||
|   Boolean = 'boolean', | ||||
|   Integer = 'integer', | ||||
|   String = 'string', | ||||
|   Choice = 'choice', | ||||
|   Model = 'related field' | ||||
| } | ||||
|  | ||||
| // Type interface defining a single 'setting' object | ||||
| export interface Setting { | ||||
|   pk: number; | ||||
|   key: string; | ||||
|   value: string; | ||||
|   name: string; | ||||
|   description: string; | ||||
|   type: SettingType; | ||||
|   units: string; | ||||
|   choices: SettingChoice[]; | ||||
|   model_name: string | null; | ||||
|   model_filters: Record<string, any> | null; | ||||
|   api_url: string | null; | ||||
|   typ: SettingTyp; | ||||
|   plugin?: string; | ||||
|   method?: string; | ||||
|   required?: boolean; | ||||
| } | ||||
|  | ||||
| export interface SettingChoice { | ||||
|   value: string; | ||||
|   display_name: string; | ||||
| } | ||||
|  | ||||
| export type SettingsLookup = { | ||||
|   [key: string]: string; | ||||
| }; | ||||
|  | ||||
| export interface SettingsStateProps { | ||||
|   settings: Setting[]; | ||||
|   lookup: SettingsLookup; | ||||
|   fetchSettings: () => Promise<boolean>; | ||||
|   endpoint: ApiEndpoints; | ||||
|   pathParams?: PathParams; | ||||
|   getSetting: (key: string, default_value?: string) => string; // Return a raw setting value | ||||
|   isSet: (key: string, default_value?: boolean) => boolean; // Check a "boolean" setting | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/frontend/lib/types/Tables.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/frontend/lib/types/Tables.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| import type { SetURLSearchParams } from 'react-router-dom'; | ||||
| import type { FilterSetState } from './Filters'; | ||||
|  | ||||
| /* | ||||
|  * Type definition for representing the state of a table: | ||||
|  * | ||||
|  * tableKey: A unique key for the table. When this key changes, the table will be refreshed. | ||||
|  * refreshTable: A callback function to externally refresh the table. | ||||
|  * isLoading: A boolean flag to indicate if the table is currently loading data | ||||
|  * setIsLoading: A function to set the isLoading flag | ||||
|  * filterSet: A group of active filters | ||||
|  * queryFilters: A map of query filters (e.g. ?active=true&overdue=false) passed in the URL | ||||
|  * setQueryFilters: A function to set the query filters | ||||
|  * clearQueryFilters: A function to clear all query filters | ||||
|  * expandedRecords: An array of expanded records (rows) in the table | ||||
|  * setExpandedRecords: A function to set the expanded records | ||||
|  * isRowExpanded: A function to determine if a record is expanded | ||||
|  * selectedRecords: An array of selected records (rows) in the table | ||||
|  * selectedIds: An array of primary key values for selected records | ||||
|  * hasSelectedRecords: A boolean flag to indicate if any records are selected | ||||
|  * setSelectedRecords: A function to set the selected records | ||||
|  * clearSelectedRecords: A function to clear all selected records | ||||
|  * hiddenColumns: An array of hidden column names | ||||
|  * setHiddenColumns: A function to set the hidden columns | ||||
|  * searchTerm: The current search term for the table | ||||
|  * setSearchTerm: A function to set the search term | ||||
|  * recordCount: The total number of records in the table | ||||
|  * setRecordCount: A function to set the record count | ||||
|  * page: The current page number | ||||
|  * setPage: A function to set the current page number | ||||
|  * pageSize: The number of records per page | ||||
|  * setPageSize: A function to set the number of records per page | ||||
|  * records: An array of records (rows) in the table | ||||
|  * setRecords: A function to set the records | ||||
|  * updateRecord: A function to update a single record in the table | ||||
|  * idAccessor: The name of the primary key field in the records (default = 'pk') | ||||
|  */ | ||||
| export type TableState = { | ||||
|   tableKey: string; | ||||
|   refreshTable: () => void; | ||||
|   isLoading: boolean; | ||||
|   setIsLoading: (value: boolean) => void; | ||||
|   filterSet: FilterSetState; | ||||
|   queryFilters: URLSearchParams; | ||||
|   setQueryFilters: SetURLSearchParams; | ||||
|   clearQueryFilters: () => void; | ||||
|   expandedRecords: any[]; | ||||
|   setExpandedRecords: (records: any[]) => void; | ||||
|   isRowExpanded: (pk: number) => boolean; | ||||
|   selectedRecords: any[]; | ||||
|   selectedIds: any[]; | ||||
|   hasSelectedRecords: boolean; | ||||
|   setSelectedRecords: (records: any[]) => void; | ||||
|   clearSelectedRecords: () => void; | ||||
|   hiddenColumns: string[]; | ||||
|   setHiddenColumns: (columns: string[]) => void; | ||||
|   searchTerm: string; | ||||
|   setSearchTerm: (term: string) => void; | ||||
|   recordCount: number; | ||||
|   setRecordCount: (count: number) => void; | ||||
|   page: number; | ||||
|   setPage: (page: number) => void; | ||||
|   pageSize: number; | ||||
|   setPageSize: (pageSize: number) => void; | ||||
|   records: any[]; | ||||
|   setRecords: (records: any[]) => void; | ||||
|   updateRecord: (record: any) => void; | ||||
|   idAccessor?: string; | ||||
| }; | ||||
							
								
								
									
										62
									
								
								src/frontend/lib/types/User.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/frontend/lib/types/User.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import type { ModelType } from '../enums/ModelType'; | ||||
| import type { UserPermissions, UserRoles } from '../enums/Roles'; | ||||
|  | ||||
| export interface UserProfile { | ||||
|   language: string; | ||||
|   theme: any; | ||||
|   widgets: any; | ||||
|   displayname: string | null; | ||||
|   position: string | null; | ||||
|   status: string | null; | ||||
|   location: string | null; | ||||
|   active: boolean; | ||||
|   contact: string | null; | ||||
|   type: string; | ||||
|   organisation: string | null; | ||||
|   primary_group: number | null; | ||||
| } | ||||
|  | ||||
| // Type interface fully defining the current user | ||||
| export interface UserProps { | ||||
|   pk: number; | ||||
|   username: string; | ||||
|   first_name: string; | ||||
|   last_name: string; | ||||
|   email: string; | ||||
|   is_staff?: boolean; | ||||
|   is_superuser?: boolean; | ||||
|   roles?: Record<string, string[]>; | ||||
|   permissions?: Record<string, string[]>; | ||||
|   groups: any[] | null; | ||||
|   profile: UserProfile; | ||||
| } | ||||
|  | ||||
| export interface UserStateProps { | ||||
|   user: UserProps | undefined; | ||||
|   is_authed: boolean; | ||||
|   userId: () => number | undefined; | ||||
|   username: () => string; | ||||
|   setAuthenticated: (authed?: boolean) => void; | ||||
|   fetchUserToken: () => Promise<void>; | ||||
|   setUser: (newUser: UserProps | undefined) => void; | ||||
|   getUser: () => UserProps | undefined; | ||||
|   fetchUserState: () => Promise<void>; | ||||
|   clearUserState: () => void; | ||||
|   checkUserRole: (role: UserRoles, permission: UserPermissions) => boolean; | ||||
|   hasDeleteRole: (role: UserRoles) => boolean; | ||||
|   hasChangeRole: (role: UserRoles) => boolean; | ||||
|   hasAddRole: (role: UserRoles) => boolean; | ||||
|   hasViewRole: (role: UserRoles) => boolean; | ||||
|   checkUserPermission: ( | ||||
|     model: ModelType, | ||||
|     permission: UserPermissions | ||||
|   ) => boolean; | ||||
|   hasDeletePermission: (model: ModelType) => boolean; | ||||
|   hasChangePermission: (model: ModelType) => boolean; | ||||
|   hasAddPermission: (model: ModelType) => boolean; | ||||
|   hasViewPermission: (model: ModelType) => boolean; | ||||
|   isAuthed: () => boolean; | ||||
|   isLoggedIn: () => boolean; | ||||
|   isStaff: () => boolean; | ||||
|   isSuperuser: () => boolean; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user