@ngockhoale/ukit 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +179 -0
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/bin/ukit +30 -0
- package/manifests/platform.full.yaml +1194 -0
- package/package.json +71 -0
- package/scripts/bug/triage.mjs +37 -0
- package/scripts/index/build-index.mjs +35 -0
- package/scripts/index/query-index.mjs +92 -0
- package/scripts/index/refresh-index.mjs +85 -0
- package/scripts/release/verify-release.mjs +56 -0
- package/src/bug/triageBug.js +123 -0
- package/src/cli/adapters.js +148 -0
- package/src/cli/commands/diff.js +51 -0
- package/src/cli/commands/doctor.js +125 -0
- package/src/cli/commands/indexArgs.js +73 -0
- package/src/cli/commands/indexTools.js +509 -0
- package/src/cli/commands/install.js +293 -0
- package/src/cli/commands/memory.js +126 -0
- package/src/cli/commands/status.js +8 -0
- package/src/cli/commands/uninstall.js +51 -0
- package/src/cli/index.js +109 -0
- package/src/context/detectProjectContext.js +49 -0
- package/src/context/detectProviders.js +12 -0
- package/src/core/applyPlan.js +89 -0
- package/src/core/buildPlan.js +228 -0
- package/src/core/compact/index.js +294 -0
- package/src/core/compact/threshold.js +936 -0
- package/src/core/diffPlan.js +73 -0
- package/src/core/ensureGitignore.js +117 -0
- package/src/core/fileOps.js +188 -0
- package/src/core/memory/hygiene.js +160 -0
- package/src/core/memory/index.js +2 -0
- package/src/core/memory/retrieval.js +476 -0
- package/src/core/memory/store.js +202 -0
- package/src/core/metadata.js +132 -0
- package/src/core/migrateLegacy.js +139 -0
- package/src/core/output/index.js +1309 -0
- package/src/core/paths.js +13 -0
- package/src/core/report.js +17 -0
- package/src/core/router/advisor.js +42 -0
- package/src/core/router/index.js +2 -0
- package/src/core/router/router.js +164 -0
- package/src/core/runInstallPipeline.js +365 -0
- package/src/core/runtimeConfig.js +190 -0
- package/src/core/runtimePaths.js +24 -0
- package/src/core/status.js +186 -0
- package/src/core/token/index.js +328 -0
- package/src/core/uninstall.js +246 -0
- package/src/core/validation/confidence.js +89 -0
- package/src/core/validation/index.js +2 -0
- package/src/core/validation/validator.js +165 -0
- package/src/index/buildIndex.js +1392 -0
- package/src/index/gitHooks.js +109 -0
- package/src/index/importResolution.js +377 -0
- package/src/index/languageTools.js +127 -0
- package/src/index/paths.js +27 -0
- package/src/index/queryIndex.js +637 -0
- package/src/index/relatedTests.js +237 -0
- package/src/index/resolveContext.js +345 -0
- package/src/index/routeCatalog.js +258 -0
- package/src/index/taskRouting.js +677 -0
- package/src/index/verificationPlan.js +437 -0
- package/src/manifest/loadManifest.js +22 -0
- package/src/manifest/selectItems.js +78 -0
- package/src/manifest/validateManifest.js +115 -0
- package/src/render/buildVariables.js +39 -0
- package/src/render/renderTemplate.js +44 -0
- package/src/stack/detectStack.js +213 -0
- package/templates/.claude/agents/bug-debugger.md +57 -0
- package/templates/.claude/agents/feature-implementer.md +55 -0
- package/templates/.claude/config/providers.md +25 -0
- package/templates/.claude/hooks/auto-allow-bash.sh +155 -0
- package/templates/.claude/hooks/auto-prune-bash.sh +75 -0
- package/templates/.claude/hooks/block-dangerous.sh +54 -0
- package/templates/.claude/hooks/compress-output.sh +17 -0
- package/templates/.claude/hooks/protect-files.sh +37 -0
- package/templates/.claude/hooks/reinject-context.sh +28 -0
- package/templates/.claude/hooks/session-start.md +13 -0
- package/templates/.claude/hooks/skill-router.sh +1681 -0
- package/templates/.claude/hooks/verification-guard.sh +271 -0
- package/templates/.claude/settings.json +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/mce/mc.xsd +75 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/pack.py +159 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/unpack.py +29 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validate.py +69 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/base.py +951 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/docx.py +274 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/.claude/skills/backend-api/SKILL.md +26 -0
- package/templates/.claude/skills/canvas-design/LICENSE.txt +202 -0
- package/templates/.claude/skills/canvas-design/SKILL.md +130 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/code-review/SKILL.md +97 -0
- package/templates/.claude/skills/debugging-toolkit/SKILL.md +156 -0
- package/templates/.claude/skills/delivery/SKILL.md +92 -0
- package/templates/.claude/skills/discover-security/SKILL.md +86 -0
- package/templates/.claude/skills/docker-packaging/SKILL.md +60 -0
- package/templates/.claude/skills/docs-manager/SKILL.md +465 -0
- package/templates/.claude/skills/docs-manager/init-project-docs.sh +70 -0
- package/templates/.claude/skills/docs-manager/templates/README.md.template +50 -0
- package/templates/.claude/skills/docs-manager/templates/agent-roles.md.template +24 -0
- package/templates/.claude/skills/docs-manager/templates/coding-conventions.md.template +28 -0
- package/templates/.claude/skills/docs-manager/templates/memory.md.template +30 -0
- package/templates/.claude/skills/docs-manager/templates/onboarding.md.template +20 -0
- package/templates/.claude/skills/docs-manager/templates/project.md.template +26 -0
- package/templates/.claude/skills/docs-quality/SKILL.md +148 -0
- package/templates/.claude/skills/docx/LICENSE.txt +30 -0
- package/templates/.claude/skills/docx/SKILL.md +197 -0
- package/templates/.claude/skills/docx/docx-js.md +350 -0
- package/templates/.claude/skills/docx/ooxml.md +610 -0
- package/templates/.claude/skills/docx/scripts/__init__.py +1 -0
- package/templates/.claude/skills/docx/scripts/document.py +1276 -0
- package/templates/.claude/skills/docx/scripts/templates/comments.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/people.xml +3 -0
- package/templates/.claude/skills/docx/scripts/utilities.py +374 -0
- package/templates/.claude/skills/duraone/SKILL.md +204 -0
- package/templates/.claude/skills/duraone/references/backend.md +636 -0
- package/templates/.claude/skills/duraone/references/frontend.md +1506 -0
- package/templates/.claude/skills/duraone/references/sql.md +631 -0
- package/templates/.claude/skills/duraone/references/workflow.md +520 -0
- package/templates/.claude/skills/executing-plans/SKILL.md +76 -0
- package/templates/.claude/skills/file-organizer/SKILL.md +433 -0
- package/templates/.claude/skills/frontend/SKILL.md +26 -0
- package/templates/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/templates/.claude/skills/frontend-design/SKILL.md +42 -0
- package/templates/.claude/skills/frontend-vue/SKILL.md +127 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Box.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Button.vue +93 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonBar.vue +29 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonFloat.vue +62 -0
- package/templates/.claude/skills/frontend-vue/components/Control/CheckButton.vue +75 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Checkbox.vue +58 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Datetime.vue +148 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Dropdownlist.vue +156 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Input.vue +106 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Label.vue +38 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Master/BoxColumn.vue +24 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Confirm.vue +33 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Info.vue +32 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/ModalInfo.vue +39 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Reject.vue +64 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Tag.vue +82 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Upload.vue +61 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/Dropdownlist.vue +103 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/PagingBar.vue +108 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/UploadImage.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AG.vue +806 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AntTable.vue +253 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditor.vue +43 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditorEnable.vue +55 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/HtmlTable.vue +40 -0
- package/templates/.claude/skills/frontend-vue/components/PDFViewer.vue +25 -0
- package/templates/.claude/skills/frontend-vue/components/Panel/FormView.vue +309 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Footer.vue +23 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Header.vue +265 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Sidebar.vue +122 -0
- package/templates/.claude/skills/frontend-vue/components/Template.vue +16 -0
- package/templates/.claude/skills/frontend-vue/components/View/Form.vue +89 -0
- package/templates/.claude/skills/frontend-vue/composables/indexDBStore.js +140 -0
- package/templates/.claude/skills/frontend-vue/composables/masterApi.js +362 -0
- package/templates/.claude/skills/frontend-vue/composables/state.js +578 -0
- package/templates/.claude/skills/frontend-vue/composables/useRequest.js +221 -0
- package/templates/.claude/skills/frontend-vue/composables/useSession.js +179 -0
- package/templates/.claude/skills/frontend-vue/composables/useTranslation.js +54 -0
- package/templates/.claude/skills/frontend-vue/composables/useWebSocket.js +257 -0
- package/templates/.claude/skills/frontend-vue/composables/userObj.js +111 -0
- package/templates/.claude/skills/frontend-vue/composables/utils.js +322 -0
- package/templates/.claude/skills/frontend-vue/reference/composables-example.vue +320 -0
- package/templates/.claude/skills/frontend-vue/reference/form-example.vue +183 -0
- package/templates/.claude/skills/frontend-vue/reference/grid-example.vue +147 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/[id].vue +106 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/index.vue +58 -0
- package/templates/.claude/skills/frontend-vue/reference/popup-example.vue +159 -0
- package/templates/.claude/skills/pdf/LICENSE.txt +30 -0
- package/templates/.claude/skills/pdf/SKILL.md +294 -0
- package/templates/.claude/skills/pdf/forms.md +205 -0
- package/templates/.claude/skills/pdf/reference.md +612 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
- package/templates/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/.claude/skills/pdf-processing/SKILL.md +107 -0
- package/templates/.claude/skills/pdf-processing-pro/FORMS.md +610 -0
- package/templates/.claude/skills/pdf-processing-pro/OCR.md +137 -0
- package/templates/.claude/skills/pdf-processing-pro/SKILL.md +296 -0
- package/templates/.claude/skills/pdf-processing-pro/TABLES.md +626 -0
- package/templates/.claude/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
- package/templates/.claude/skills/postgres/SKILL.md +69 -0
- package/templates/.claude/skills/postgres/reference/fn_get_examples.sql +208 -0
- package/templates/.claude/skills/postgres/reference/fn_rpt_examples.sql +239 -0
- package/templates/.claude/skills/postgres/reference/utility_functions.sql +94 -0
- package/templates/.claude/skills/pptx/LICENSE.txt +30 -0
- package/templates/.claude/skills/pptx/SKILL.md +484 -0
- package/templates/.claude/skills/pptx/html2pptx.md +625 -0
- package/templates/.claude/skills/pptx/ooxml.md +427 -0
- package/templates/.claude/skills/pptx/scripts/html2pptx.js +979 -0
- package/templates/.claude/skills/pptx/scripts/inventory.py +1020 -0
- package/templates/.claude/skills/pptx/scripts/rearrange.py +231 -0
- package/templates/.claude/skills/pptx/scripts/replace.py +385 -0
- package/templates/.claude/skills/pptx/scripts/thumbnail.py +450 -0
- package/templates/.claude/skills/repo-maintenance/SKILL.md +97 -0
- package/templates/.claude/skills/research/EXAMPLES.md +434 -0
- package/templates/.claude/skills/research/REFERENCE.md +399 -0
- package/templates/.claude/skills/research/SKILL.md +136 -0
- package/templates/.claude/skills/root-cause-tracing/SKILL.md +174 -0
- package/templates/.claude/skills/root-cause-tracing/find-polluter.sh +63 -0
- package/templates/.claude/skills/sharing-skills/SKILL.md +194 -0
- package/templates/.claude/skills/sql-optimization-patterns/SKILL.md +493 -0
- package/templates/.claude/skills/subagent-driven-development/SKILL.md +189 -0
- package/templates/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/templates/.claude/skills/systematic-debugging/SKILL.md +295 -0
- package/templates/.claude/skills/systematic-debugging/test-academic.md +14 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/templates/.claude/skills/test-driven-development/SKILL.md +364 -0
- package/templates/.claude/skills/testing-anti-patterns/SKILL.md +302 -0
- package/templates/.claude/skills/testing-quality/SKILL.md +97 -0
- package/templates/.claude/skills/verification-before-completion/SKILL.md +139 -0
- package/templates/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/templates/.claude/skills/webapp-testing/SKILL.md +96 -0
- package/templates/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/templates/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/templates/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/templates/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/templates/.claude/ukit/index/build-index.mjs +28 -0
- package/templates/.claude/ukit/index/cache-utils.mjs +140 -0
- package/templates/.claude/ukit/index/lib/index-core.mjs +2800 -0
- package/templates/.claude/ukit/index/query-index.mjs +150 -0
- package/templates/.claude/ukit/index/refresh-index.mjs +57 -0
- package/templates/.claude/ukit/index/reset-auto-permissions.mjs +76 -0
- package/templates/.claude/ukit/index/resolve-context.mjs +279 -0
- package/templates/.claude/ukit/index/route-catalog.mjs +258 -0
- package/templates/.claude/ukit/index/route-task.mjs +1994 -0
- package/templates/.claude/ukit/index/triage.mjs +133 -0
- package/templates/.claude/ukit/index/verify-context.mjs +689 -0
- package/templates/.claude/ukit/runtime/compact-threshold.mjs +1013 -0
- package/templates/.claude/ukit/runtime/output-compression.mjs +1340 -0
- package/templates/.claude/ukit/runtime/reinject-context.mjs +874 -0
- package/templates/.claude/ukit/runtime/token-utils.mjs +500 -0
- package/templates/.codex/README.md +83 -0
- package/templates/.codex/settings.json +187 -0
- package/templates/.gitignore +75 -0
- package/templates/AGENTS.md +116 -0
- package/templates/CLAUDE.md +93 -0
- package/templates/adapter-presets/antigravity/README.md +22 -0
- package/templates/adapter-presets/antigravity/rules.md +49 -0
- package/templates/adapter-presets/claude/settings.local.json +42 -0
- package/templates/adapter-presets/codex/settings.local.json +6 -0
- package/templates/adapter-presets/opencode/opencode.template.json +1 -0
- package/templates/docs/BUGFIX.md +20 -0
- package/templates/docs/BUG_INDEX.md +12 -0
- package/templates/docs/BUG_METRICS.md +7 -0
- package/templates/docs/BUG_TEMPLATE.md +13 -0
- package/templates/docs/CODE_MAP.md +35 -0
- package/templates/docs/INSTALL.md +113 -0
- package/templates/docs/MEMORY.md +49 -0
- package/templates/docs/PROJECT.md +50 -0
- package/templates/docs/UKIT_USAGE_GUIDE.md +147 -0
- package/templates/docs/WORKLOG.md +10 -0
- package/templates/ukit/README.md +14 -0
- package/templates/ukit/storage/cache/compact-history.json +3 -0
- package/templates/ukit/storage/cache/compact-pressure.json +1 -0
- package/templates/ukit/storage/cache/output-history.json +3 -0
- package/templates/ukit/storage/cache/prompt-cache.json +3 -0
- package/templates/ukit/storage/config.json +37 -0
- package/templates/ukit/storage/memory/projects/.gitkeep +2 -0
- package/templates/ukit/storage/memory/sessions/.gitkeep +0 -0
- package/templates/ukit/storage/memory/user.json +5 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useWebSocket.js
|
|
3
|
+
* @description WebSocket composable — quản lý kết nối realtime với auto-retry.
|
|
4
|
+
*
|
|
5
|
+
* Có 2 hàm chính:
|
|
6
|
+
* - useWebSocket() → Composable đầy đủ (auto connect onMounted, disconnect onUnmounted, retry, debug)
|
|
7
|
+
* - subscribeWebSocket() → Phiên bản đơn giản, chỉ cần truyền URL + callback onMessage
|
|
8
|
+
*/
|
|
9
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Vue composable quản lý WebSocket connection với auto-retry.
|
|
13
|
+
* Tự connect khi component mounted, disconnect khi unmounted.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} url - WebSocket URL (vd: "wss://qas.duraoneportal.com/ws")
|
|
16
|
+
* @param {Object} [options={}] - Cấu hình
|
|
17
|
+
* @param {number} [options.maxRetries=5] - Số lần retry tối đa khi mất kết nối
|
|
18
|
+
* @param {number} [options.retryDelay=3000] - Khoảng cách giữa các lần retry (ms)
|
|
19
|
+
* @returns {Object} WebSocket controls:
|
|
20
|
+
* - connect() → Kết nối thủ công
|
|
21
|
+
* - disconnect() → Ngắt kết nối
|
|
22
|
+
* - send(data) → Gửi data (auto JSON.stringify), trả true/false
|
|
23
|
+
* - setupEventListeners(onMsg) → Đăng ký callback nhận message (data đã parse JSON)
|
|
24
|
+
* - isConnected {Ref<boolean>} → Trạng thái kết nối
|
|
25
|
+
* - error {Ref<string|null>} → Lỗi hiện tại
|
|
26
|
+
* - retryCount {Ref<number>} → Số lần đã retry
|
|
27
|
+
* - connectionStatus {Ref<string>} → 'initial'|'connecting'|'connected'|'disconnected'|'error'
|
|
28
|
+
* - lastError {Ref<Object|null>} → Chi tiết lỗi cuối cùng
|
|
29
|
+
* - getDebugInfo() → Object debug info đầy đủ
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const { send, isConnected, setupEventListeners } = useWebSocket("wss://example.com/ws")
|
|
33
|
+
* setupEventListeners((data) => { console.log("Received:", data) })
|
|
34
|
+
* send({ type: "ping" })
|
|
35
|
+
*/
|
|
36
|
+
export function useWebSocket(url, options = {}) {
|
|
37
|
+
const socket = ref(null)
|
|
38
|
+
const isConnected = ref(false)
|
|
39
|
+
const error = ref(null)
|
|
40
|
+
const retryCount = ref(0)
|
|
41
|
+
const MAX_RETRIES = options.maxRetries || 5
|
|
42
|
+
const RETRY_DELAY = options.retryDelay || 3000
|
|
43
|
+
const connectionStatus = ref('initial') // 'initial', 'connecting', 'connected', 'disconnected', 'error'
|
|
44
|
+
const lastError = ref(null)
|
|
45
|
+
|
|
46
|
+
// Debug logging function
|
|
47
|
+
const debugLog = (type, message, data = null) => {
|
|
48
|
+
const timestamp = new Date().toISOString()
|
|
49
|
+
console.log(`[WebSocket ${type}] ${timestamp} - ${message}`, data)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const connect = () => {
|
|
53
|
+
if (!url) {
|
|
54
|
+
debugLog('ERROR', 'WebSocket URL is required')
|
|
55
|
+
error.value = 'WebSocket URL is required'
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
connectionStatus.value = 'connecting'
|
|
60
|
+
debugLog('INFO', `Attempting to connect to: ${url}`)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
socket.value = new WebSocket(url)
|
|
64
|
+
|
|
65
|
+
socket.value.onopen = () => {
|
|
66
|
+
connectionStatus.value = 'connected'
|
|
67
|
+
debugLog('SUCCESS', 'WebSocket connection opened successfully')
|
|
68
|
+
isConnected.value = true
|
|
69
|
+
retryCount.value = 0
|
|
70
|
+
error.value = null
|
|
71
|
+
lastError.value = null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
socket.value.onclose = (event) => {
|
|
75
|
+
connectionStatus.value = 'disconnected'
|
|
76
|
+
debugLog('ERROR', 'WebSocket connection closed', event)
|
|
77
|
+
isConnected.value = false
|
|
78
|
+
error.value = `WebSocket closed: ${event.code} - ${event.reason}`
|
|
79
|
+
lastError.value = {
|
|
80
|
+
code: event.code,
|
|
81
|
+
reason: event.reason,
|
|
82
|
+
wasClean: event.wasClean
|
|
83
|
+
}
|
|
84
|
+
retryConnection()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
socket.value.onerror = (err) => {
|
|
88
|
+
connectionStatus.value = 'error'
|
|
89
|
+
debugLog('ERROR', 'WebSocket error', err)
|
|
90
|
+
error.value = `WebSocket error: ${err.message}`
|
|
91
|
+
lastError.value = err
|
|
92
|
+
retryConnection()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
socket.value.onmessage = (event) => {
|
|
96
|
+
debugLog('MESSAGE', 'Received message', event.data)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
} catch (err) {
|
|
100
|
+
connectionStatus.value = 'error'
|
|
101
|
+
debugLog('ERROR', 'Failed to create WebSocket', err)
|
|
102
|
+
error.value = `Failed to create WebSocket: ${err.message}`
|
|
103
|
+
lastError.value = err
|
|
104
|
+
retryConnection()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const retryConnection = () => {
|
|
109
|
+
if (retryCount.value < MAX_RETRIES) {
|
|
110
|
+
retryCount.value++
|
|
111
|
+
debugLog('INFO', `Retrying connection (${retryCount.value}/${MAX_RETRIES})...`)
|
|
112
|
+
setTimeout(connect, RETRY_DELAY)
|
|
113
|
+
} else {
|
|
114
|
+
connectionStatus.value = 'error'
|
|
115
|
+
debugLog('ERROR', 'Max retry attempts reached')
|
|
116
|
+
error.value = 'Max retry attempts reached'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const getDebugInfo = () => ({
|
|
121
|
+
isConnected: isConnected.value,
|
|
122
|
+
error: error.value,
|
|
123
|
+
retryCount: retryCount.value,
|
|
124
|
+
url,
|
|
125
|
+
options,
|
|
126
|
+
connectionStatus: connectionStatus.value,
|
|
127
|
+
socketStatus: socket.value ? socket.value.readyState : null,
|
|
128
|
+
lastError: lastError.value
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
onMounted(connect)
|
|
132
|
+
onUnmounted(() => {
|
|
133
|
+
if (socket.value) {
|
|
134
|
+
debugLog('INFO', 'Disconnecting WebSocket')
|
|
135
|
+
socket.value.close()
|
|
136
|
+
socket.value = null
|
|
137
|
+
isConnected.value = false
|
|
138
|
+
error.value = null
|
|
139
|
+
connectionStatus.value = 'disconnected'
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
connect,
|
|
145
|
+
disconnect: () => {
|
|
146
|
+
if (socket.value) {
|
|
147
|
+
debugLog('INFO', 'Disconnecting WebSocket')
|
|
148
|
+
socket.value.close()
|
|
149
|
+
socket.value = null
|
|
150
|
+
isConnected.value = false
|
|
151
|
+
error.value = null
|
|
152
|
+
connectionStatus.value = 'disconnected'
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
send: (data) => {
|
|
156
|
+
if (!socket.value || !isConnected.value) {
|
|
157
|
+
debugLog('ERROR', 'WebSocket is not connected')
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
debugLog('MESSAGE', 'Sending message', data)
|
|
163
|
+
socket.value.send(JSON.stringify(data))
|
|
164
|
+
return true
|
|
165
|
+
} catch (err) {
|
|
166
|
+
debugLog('ERROR', 'Failed to send message', err)
|
|
167
|
+
error.value = `Failed to send message: ${err.message}`
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
setupEventListeners: (onMessage) => {
|
|
172
|
+
if (socket.value) {
|
|
173
|
+
socket.value.onmessage = (event) => {
|
|
174
|
+
try {
|
|
175
|
+
const data = JSON.parse(event.data)
|
|
176
|
+
debugLog('MESSAGE', 'Parsed message', data)
|
|
177
|
+
onMessage(data)
|
|
178
|
+
} catch (error) {
|
|
179
|
+
debugLog('ERROR', 'Error parsing message', error)
|
|
180
|
+
error.value = `Error parsing message: ${error.message}`
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
isConnected,
|
|
186
|
+
error,
|
|
187
|
+
retryCount,
|
|
188
|
+
getDebugInfo,
|
|
189
|
+
connectionStatus,
|
|
190
|
+
lastError
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Phiên bản đơn giản của WebSocket — chỉ subscribe nhận message.
|
|
196
|
+
* KHÔNG có auto-retry, KHÔNG bind vào lifecycle component.
|
|
197
|
+
* Phải gọi disconnect() thủ công khi không dùng nữa.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} url - WebSocket URL
|
|
200
|
+
* @param {function(Object): void} onMessage - Callback nhận message (data đã parse JSON)
|
|
201
|
+
* @returns {Object} { isConnected: Ref<boolean>, disconnect: Function }
|
|
202
|
+
* @example
|
|
203
|
+
* const { isConnected, disconnect } = subscribeWebSocket("wss://example.com/ws", (data) => {
|
|
204
|
+
* console.log("New message:", data)
|
|
205
|
+
* })
|
|
206
|
+
* // Khi không dùng nữa:
|
|
207
|
+
* disconnect()
|
|
208
|
+
*/
|
|
209
|
+
export function subscribeWebSocket(url, onMessage) {
|
|
210
|
+
const socket = ref(null)
|
|
211
|
+
const isConnected = ref(false)
|
|
212
|
+
|
|
213
|
+
// Create WebSocket connection
|
|
214
|
+
socket.value = new WebSocket(url)
|
|
215
|
+
|
|
216
|
+
// Handle connection open
|
|
217
|
+
socket.value.onopen = () => {
|
|
218
|
+
console.log('WebSocket connected')
|
|
219
|
+
isConnected.value = true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Handle incoming messages
|
|
223
|
+
socket.value.onmessage = (event) => {
|
|
224
|
+
console.log('Received message:', event.data)
|
|
225
|
+
try {
|
|
226
|
+
const data = JSON.parse(event.data)
|
|
227
|
+
onMessage(data)
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('Error parsing message:', error)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle connection close
|
|
234
|
+
socket.value.onclose = () => {
|
|
235
|
+
console.log('WebSocket disconnected')
|
|
236
|
+
isConnected.value = false
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Handle errors
|
|
240
|
+
socket.value.onerror = (error) => {
|
|
241
|
+
console.error('WebSocket error:', error)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Return cleanup function
|
|
245
|
+
const disconnect = () => {
|
|
246
|
+
if (socket.value) {
|
|
247
|
+
socket.value.close()
|
|
248
|
+
socket.value = null
|
|
249
|
+
isConnected.value = false
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
isConnected,
|
|
255
|
+
disconnect
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file userObj.js
|
|
3
|
+
* @description Các hàm truy vấn thông tin nhân viên và gửi notification.
|
|
4
|
+
* Dữ liệu từ table "nhanvien" (nhân viên).
|
|
5
|
+
*
|
|
6
|
+
* @requires request - từ useRequest.js
|
|
7
|
+
* @requires get_schema - từ state.js
|
|
8
|
+
* @requires dayjs - từ utils.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Lấy toàn bộ thông tin nhân viên theo username
|
|
13
|
+
* @param {string} manhanvien - Username (mã nhân viên)
|
|
14
|
+
* @returns {Promise<Object>} Object nhân viên hoặc {} nếu không tìm thấy
|
|
15
|
+
* @example const user = await getUserObjByUsername("nguyenvana")
|
|
16
|
+
*/
|
|
17
|
+
export const getUserObjByUsername = async (manhanvien) => {
|
|
18
|
+
let to_return = {};
|
|
19
|
+
let data = {
|
|
20
|
+
schema: get_schema(),
|
|
21
|
+
table: "nhanvien",
|
|
22
|
+
conditions: JSON.stringify({ username: manhanvien }),
|
|
23
|
+
};
|
|
24
|
+
const resp = await request("/select", data, "get");
|
|
25
|
+
if (resp.length > 0) {
|
|
26
|
+
to_return = resp[0];
|
|
27
|
+
}
|
|
28
|
+
return to_return;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Lấy mã sales supervisor của nhân viên
|
|
33
|
+
* @param {string} manhanvien - Username
|
|
34
|
+
* @returns {Promise<string>} Mã salesup hoặc "" nếu không tìm thấy
|
|
35
|
+
*/
|
|
36
|
+
export const getSalesupByUsername = async (manhanvien) => {
|
|
37
|
+
let masalesup = "";
|
|
38
|
+
let data = {
|
|
39
|
+
schema: get_schema(),
|
|
40
|
+
table: "nhanvien",
|
|
41
|
+
columns: ["masalesup"],
|
|
42
|
+
conditions: JSON.stringify({ username: manhanvien }),
|
|
43
|
+
};
|
|
44
|
+
const resp = await request("/select", data, "get");
|
|
45
|
+
if (resp.length > 0) {
|
|
46
|
+
masalesup = resp[0].masalesup;
|
|
47
|
+
}
|
|
48
|
+
return masalesup;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Lấy tên đầy đủ nhân viên theo username
|
|
53
|
+
* @param {string} manhanvien - Username
|
|
54
|
+
* @returns {Promise<string>} Tên nhân viên hoặc "" nếu không tìm thấy
|
|
55
|
+
*/
|
|
56
|
+
export const getFullnameByUsername = async (manhanvien) => {
|
|
57
|
+
let tennhanvien = "";
|
|
58
|
+
let data = {
|
|
59
|
+
schema: get_schema(),
|
|
60
|
+
table: "nhanvien",
|
|
61
|
+
columns: ["tennhanvien"],
|
|
62
|
+
conditions: JSON.stringify({ username: manhanvien }),
|
|
63
|
+
};
|
|
64
|
+
const resp = await request("/select", data, "get");
|
|
65
|
+
if (resp.length > 0) {
|
|
66
|
+
tennhanvien = resp[0].tennhanvien;
|
|
67
|
+
}
|
|
68
|
+
return tennhanvien;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gửi push notification đến user qua cả Android và iOS.
|
|
73
|
+
* Tự lấy danh sách token thiết bị, gửi noti, và lưu lịch sử vào DB.
|
|
74
|
+
* @param {string} username - Username người nhận
|
|
75
|
+
* @param {Object} [data={}] - Nội dung notification
|
|
76
|
+
* @param {string} data.title - Tiêu đề notification
|
|
77
|
+
* @param {string} data.message - Nội dung notification
|
|
78
|
+
* @example await sendNotiToUser("nguyenvana", { title: "Thông báo", message: "Bạn có agreement mới" })
|
|
79
|
+
*/
|
|
80
|
+
export const sendNotiToUser = async (username, data = {}) => {
|
|
81
|
+
let noti_send = data;
|
|
82
|
+
let notiobj = await get_noti_token_list(username);
|
|
83
|
+
const android_list = notiobj.android;
|
|
84
|
+
const ios_list = notiobj.ios;
|
|
85
|
+
noti_send['os'] = 'android3';
|
|
86
|
+
noti_send['token'] = android_list;
|
|
87
|
+
requestSendnoti(noti_send);
|
|
88
|
+
noti_send['os'] = 'ios';
|
|
89
|
+
noti_send['token'] = ios_list;
|
|
90
|
+
requestSendnoti(noti_send);
|
|
91
|
+
let to_save_noti = {
|
|
92
|
+
username: username,
|
|
93
|
+
title: data.title,
|
|
94
|
+
message: data.message,
|
|
95
|
+
os: data.os,
|
|
96
|
+
token: data.token,
|
|
97
|
+
}
|
|
98
|
+
saveNotiToServer(to_save_noti);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Lưu lịch sử notification vào DB (table "notification")
|
|
103
|
+
* @param {Object} [data={}] - { username, title, message, os, token }
|
|
104
|
+
* @returns {Promise<Object>}
|
|
105
|
+
*/
|
|
106
|
+
export const saveNotiToServer = async (data = {}) => {
|
|
107
|
+
data['send_time'] = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
|
108
|
+
data['schema'] = get_schema();
|
|
109
|
+
data['table'] = 'notification';
|
|
110
|
+
await request("/save", data);
|
|
111
|
+
};
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file utils.js
|
|
3
|
+
* @description Utility functions dùng chung — auto-import toàn cục.
|
|
4
|
+
* Gồm: format date/number, validation, navigation, UI helpers, data transformation.
|
|
5
|
+
*
|
|
6
|
+
* Tất cả đều là pure functions (trừ go_to_page, go_back, show_message dùng side-effect).
|
|
7
|
+
*/
|
|
8
|
+
import _dayjs from 'dayjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wrapper dayjs — dùng thay cho import dayjs trực tiếp
|
|
12
|
+
* @param {...*} args - Tham số truyền vào dayjs (date string, Date object, ...)
|
|
13
|
+
* @returns {import('dayjs').Dayjs} dayjs instance
|
|
14
|
+
*/
|
|
15
|
+
export function dayjs(...args) {
|
|
16
|
+
return _dayjs(...args)
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Format ngày tháng theo type chỉ định
|
|
21
|
+
* @param {string|Date} date - Ngày cần format
|
|
22
|
+
* @param {"time"|"datetime"|"date"|"read_date"|"read_datetime"} [type] - Kiểu format
|
|
23
|
+
* - "time" → "HH:mm:ss"
|
|
24
|
+
* - "datetime" → "YYYY-MM-DD HH:mm:ss"
|
|
25
|
+
* - "date" → "YYYY-MM-DD"
|
|
26
|
+
* - "read_date" → "DD/MM/YYYY" (hiển thị cho user)
|
|
27
|
+
* - "read_datetime" → "DD/MM/YYYY HH:mm:ss"
|
|
28
|
+
* - mặc định → "YYYY-MM-DD"
|
|
29
|
+
* @returns {string} Chuỗi ngày đã format, trả "" nếu date null/blank
|
|
30
|
+
*/
|
|
31
|
+
export const formatDate = (date, type) => {
|
|
32
|
+
if (check_is_null_or_blank(date)) {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
if (type === "time") {
|
|
36
|
+
return dayjs(date).format("HH:mm:ss");
|
|
37
|
+
}
|
|
38
|
+
if (type === "datetime") {
|
|
39
|
+
return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
|
|
40
|
+
}
|
|
41
|
+
if (type === "date") {
|
|
42
|
+
return dayjs(date).format("YYYY-MM-DD");
|
|
43
|
+
}
|
|
44
|
+
if (type === "read_date") {
|
|
45
|
+
return dayjs(date).format("DD/MM/YYYY");
|
|
46
|
+
}
|
|
47
|
+
if (type === "read_datetime") {
|
|
48
|
+
return dayjs(date).format("DD/MM/YYYY HH:mm:ss");
|
|
49
|
+
}
|
|
50
|
+
return dayjs(date).format("YYYY-MM-DD");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Tạo mảng các ngày liên tiếp từ fromDate đến toDate
|
|
55
|
+
* @param {string|Date} fromDate - Ngày bắt đầu
|
|
56
|
+
* @param {string|Date} toDate - Ngày kết thúc
|
|
57
|
+
* @returns {Array<string>} Mảng ngày format "YYYY-MM-DD"
|
|
58
|
+
* @example getDateList("2024-01-01", "2024-01-03") → ["2024-01-01", "2024-01-02", "2024-01-03"]
|
|
59
|
+
*/
|
|
60
|
+
export const getDateList = (fromDate, toDate) => {
|
|
61
|
+
const end = dayjs(toDate);
|
|
62
|
+
const dateArray = [];
|
|
63
|
+
let currentDate = dayjs(fromDate);
|
|
64
|
+
while (currentDate.isBefore(end) || currentDate.isSame(end, "day")) {
|
|
65
|
+
let formattedDate = currentDate.format("YYYY-MM-DD");
|
|
66
|
+
dateArray.push(formattedDate);
|
|
67
|
+
currentDate = currentDate.add(1, "day");
|
|
68
|
+
}
|
|
69
|
+
return dateArray;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format số với dấu phân cách hàng nghìn và số thập phân
|
|
74
|
+
* @param {number|string} num - Số cần format
|
|
75
|
+
* @param {string} [separator="."] - Ký tự phân cách hàng nghìn
|
|
76
|
+
* @param {number} [decimal=2] - Số chữ số thập phân
|
|
77
|
+
* @returns {string} Số đã format, trả "" nếu num null/blank
|
|
78
|
+
* @example formatNumber(1234567.8) → "1.234.567.80"
|
|
79
|
+
*/
|
|
80
|
+
export const formatNumber = (num, separator = ".", decimal = 2) => {
|
|
81
|
+
if (check_is_null_or_blank(num)) {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
num = parseFloat(num);
|
|
85
|
+
return num.toFixed(decimal).replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Lấy chiều cao màn hình (chỉ chạy client-side)
|
|
90
|
+
* @returns {number|undefined} window.innerHeight
|
|
91
|
+
*/
|
|
92
|
+
export const screen_height = () => {
|
|
93
|
+
if (process.client) {
|
|
94
|
+
return window.innerHeight;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Kiểm tra arr1 có chứa tất cả phần tử của arr2 không
|
|
100
|
+
* @param {Array} arr1 - Mảng nguồn
|
|
101
|
+
* @param {Array} arr2 - Mảng cần kiểm tra
|
|
102
|
+
* @returns {boolean} true nếu arr1 chứa tất cả phần tử arr2
|
|
103
|
+
*/
|
|
104
|
+
export const arraysContainAllElements = (arr1, arr2) => {
|
|
105
|
+
return arr2.every((element) => arr1.includes(element));
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Bỏ dấu tiếng Việt — chuyển chuỗi có dấu thành không dấu (dùng cho search/filter)
|
|
110
|
+
* @param {string} str - Chuỗi tiếng Việt có dấu
|
|
111
|
+
* @returns {string} Chuỗi lowercase không dấu, đã loại ký tự đặc biệt
|
|
112
|
+
* @example bodautiengviet("Nguyễn Văn A") → "nguyen van a"
|
|
113
|
+
*/
|
|
114
|
+
export const bodautiengviet = (str) => {
|
|
115
|
+
str = str.toString().toLowerCase();
|
|
116
|
+
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, "a");
|
|
117
|
+
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, "e");
|
|
118
|
+
str = str.replace(/ì|í|ị|ỉ|ĩ/g, "i");
|
|
119
|
+
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, "o");
|
|
120
|
+
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, "u");
|
|
121
|
+
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, "y");
|
|
122
|
+
str = str.replace(/đ/g, "d");
|
|
123
|
+
str = str.replace(/!|@|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\;|\'|\"|\&|\#|\[|\]|~|\$|_|`|-|{|}|\||\\/g, " ");
|
|
124
|
+
str = str.replace(/ + /g, " ");
|
|
125
|
+
str = str.trim();
|
|
126
|
+
return str;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Loại bỏ thẻ <a> trong các cột chỉ định của danh sách (mutate trực tiếp)
|
|
131
|
+
* @param {Array<Object>} lst - Mảng objects cần xử lý
|
|
132
|
+
* @param {Array<string>} col - Danh sách tên cột cần loại HTML
|
|
133
|
+
* @returns {Array<Object>} Mảng đã xử lý (cùng reference)
|
|
134
|
+
*/
|
|
135
|
+
export const remove_html = (lst, col) => {
|
|
136
|
+
for (let i = 0; i < lst.length; i++) {
|
|
137
|
+
for (let j = 0; j < col.length; j++) {
|
|
138
|
+
if (!check_is_null_or_blank(lst[i][col[j]])) {
|
|
139
|
+
if (typeof lst[i][col[j]] === "string") {
|
|
140
|
+
if (lst[i][col[j]].includes("<a")) {
|
|
141
|
+
lst[i][col[j]] = remove_a_tag(lst[i][col[j]]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return lst;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Kiểm tra giá trị null, undefined, rỗng, hoặc object/array trống
|
|
152
|
+
* @param {*} value - Giá trị cần kiểm tra
|
|
153
|
+
* @returns {boolean} true nếu value là null/undefined/""/[]/{}
|
|
154
|
+
* @example
|
|
155
|
+
* check_is_null_or_blank(null) → true
|
|
156
|
+
* check_is_null_or_blank("") → true
|
|
157
|
+
* check_is_null_or_blank([]) → true
|
|
158
|
+
* check_is_null_or_blank({}) → true
|
|
159
|
+
* check_is_null_or_blank("abc") → false
|
|
160
|
+
*/
|
|
161
|
+
export const check_is_null_or_blank = (value) => {
|
|
162
|
+
if (value == null) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
let to_return = typeof value === "undefined" || value === "undefined" || value === undefined || value === "" || value == {};
|
|
166
|
+
if (typeof value === "object") {
|
|
167
|
+
if (value.length == 0) {
|
|
168
|
+
to_return = true;
|
|
169
|
+
}
|
|
170
|
+
if (Object.keys(value).length === 0) {
|
|
171
|
+
to_return = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return to_return;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Kiểm tra giá trị bằng 0 (bao gồm cả string "0", "0.00")
|
|
179
|
+
* @param {*} value - Giá trị cần kiểm tra
|
|
180
|
+
* @returns {boolean} true nếu value là 0/"0"/"0.00"/undefined
|
|
181
|
+
*/
|
|
182
|
+
export const check_is_zero = (value) => {
|
|
183
|
+
if (isNaN(value)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return typeof value === "undefined" || value === "0" || value === "0.00" || value === 0 || value == 0;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Cuộn trang lên đầu (smooth scroll)
|
|
191
|
+
*/
|
|
192
|
+
export const move_to_top = () => {
|
|
193
|
+
window.scrollTo({
|
|
194
|
+
top: 0,
|
|
195
|
+
behavior: "smooth",
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Chuyển trang bằng Vue Router
|
|
201
|
+
* @param {string} page - Đường dẫn trang (vd: "/master/branch")
|
|
202
|
+
* @param {Object|string} [query=""] - Query params (vd: { id: 123 })
|
|
203
|
+
*/
|
|
204
|
+
export const go_to_page = (page, query = "") => {
|
|
205
|
+
const router = useRouter();
|
|
206
|
+
if (query === "") {
|
|
207
|
+
router.push({ path: page });
|
|
208
|
+
} else {
|
|
209
|
+
router.push({ path: page, query: query });
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Quay lại trang trước. Nếu không có history → về page chỉ định hoặc "/"
|
|
215
|
+
* @param {string} [page] - Trang fallback nếu không có history
|
|
216
|
+
*/
|
|
217
|
+
export const go_back = (page) => {
|
|
218
|
+
const router = useRouter();
|
|
219
|
+
if (window.history.length > 1) {
|
|
220
|
+
router.back(); // Go back to the previous page
|
|
221
|
+
} else {
|
|
222
|
+
if (check_is_null_or_blank(page)) {
|
|
223
|
+
go_to_page("/");
|
|
224
|
+
} else {
|
|
225
|
+
go_to_page(page);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Tạm dừng thực thi trong khoảng thời gian chỉ định
|
|
232
|
+
* @param {number} ms - Thời gian chờ (milliseconds)
|
|
233
|
+
* @returns {Promise<void>}
|
|
234
|
+
* @example await sleep(1000) // chờ 1 giây
|
|
235
|
+
*/
|
|
236
|
+
export const sleep = (ms) => {
|
|
237
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
import { message } from "ant-design-vue";
|
|
241
|
+
/**
|
|
242
|
+
* Hiển thị thông báo toast (Ant Design Vue message)
|
|
243
|
+
* @param {"success"|"error"|"warning"|"info"|"loading"} type - Loại thông báo
|
|
244
|
+
* @param {string} content - Nội dung hiển thị
|
|
245
|
+
* @param {number} [duration=3] - Thời gian hiển thị (giây)
|
|
246
|
+
* @example show_message("success", "Lưu thành công!")
|
|
247
|
+
* @example show_message("error", "Có lỗi xảy ra", 5)
|
|
248
|
+
*/
|
|
249
|
+
export const show_message = (type, content, duration = 3) => {
|
|
250
|
+
message[type]({
|
|
251
|
+
content: content,
|
|
252
|
+
duration: duration,
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Chuyển đổi mảng object thành format dropdown {value, label}
|
|
258
|
+
* Label tự động dịch qua hàm t().
|
|
259
|
+
* @param {Array<Object>} lst - Mảng dữ liệu gốc
|
|
260
|
+
* @param {string} value_col - Tên cột làm value
|
|
261
|
+
* @param {string} label_col - Tên cột làm label
|
|
262
|
+
* @returns {Array<{value: *, label: string}>} Mảng dropdown options
|
|
263
|
+
* @example
|
|
264
|
+
* convertToDropdownValue([{code: "VN", name: "Vietnam"}], "code", "name")
|
|
265
|
+
* → [{value: "VN", label: "Vietnam"}]
|
|
266
|
+
*/
|
|
267
|
+
export const convertToDropdownValue = (lst, value_col, label_col) => {
|
|
268
|
+
let to_return = lst.map((obj) => {
|
|
269
|
+
return {
|
|
270
|
+
value: obj[value_col],
|
|
271
|
+
label: t(obj[label_col]),
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
return to_return;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Chuyển mảng objects thành object key-value (lookup map)
|
|
279
|
+
* @param {Array<Object>} lst - Mảng dữ liệu gốc
|
|
280
|
+
* @param {string} key_col - Tên cột làm key
|
|
281
|
+
* @param {string} value_col - Tên cột làm value
|
|
282
|
+
* @returns {Object} Object dạng { [key]: value }
|
|
283
|
+
* @example
|
|
284
|
+
* convertListToObject([{id: 1, name: "A"}, {id: 2, name: "B"}], "id", "name")
|
|
285
|
+
* → { 1: "A", 2: "B" }
|
|
286
|
+
*/
|
|
287
|
+
export const convertListToObject = (lst, key_col, value_col) => {
|
|
288
|
+
let to_return = {};
|
|
289
|
+
lst.map((obj) => {
|
|
290
|
+
to_return[obj[key_col]] = obj[value_col];
|
|
291
|
+
});
|
|
292
|
+
return to_return;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Trích xuất 1 field từ mảng objects thành mảng giá trị (pluck)
|
|
297
|
+
* @param {Array<Object>} lst - Mảng dữ liệu gốc
|
|
298
|
+
* @param {string} key_col - Tên cột cần trích
|
|
299
|
+
* @returns {Array} Mảng giá trị của field đó
|
|
300
|
+
* @example
|
|
301
|
+
* convertListToListOfField([{id: 1, name: "A"}, {id: 2, name: "B"}], "name")
|
|
302
|
+
* → ["A", "B"]
|
|
303
|
+
*/
|
|
304
|
+
export const convertListToListOfField = (lst, key_col) => {
|
|
305
|
+
let to_return = [];
|
|
306
|
+
lst.map((obj) => {
|
|
307
|
+
to_return.push(obj[key_col]);
|
|
308
|
+
});
|
|
309
|
+
return to_return;
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* Sắp xếp mảng dropdown theo value tăng dần (parse int)
|
|
313
|
+
* @param {Array<{value: string|number}>} list - Mảng dropdown options
|
|
314
|
+
* @returns {Array<{value: string|number}>} Mảng đã sắp xếp
|
|
315
|
+
*/
|
|
316
|
+
export function sortListByValueAsc(list) {
|
|
317
|
+
return list.sort((a, b) => {
|
|
318
|
+
const aValue = parseInt(a.value);
|
|
319
|
+
const bValue = parseInt(b.value);
|
|
320
|
+
return aValue - bValue;
|
|
321
|
+
});
|
|
322
|
+
}
|