Dev cleaning (#106)
This commit is contained in:
No files matched your search
@@ -0,0 +1,30 @@
|
|||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Default settings for all files
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# For Markdown files, don't trim trailing whitespace (karena kadang dipakai untuk line break)
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# For JSON, YAML, and config files
|
||||||
|
[*.{json,yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# For JS, TS, Vue files
|
||||||
|
[*.{js,ts,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# For CSS, SCSS, PostCSS
|
||||||
|
[*.{css,scss,pcss}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
API_ORIGIN=
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": false,
|
||||||
|
"plugins": ["prettier-plugin-tailwindcss"],
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
|
"singleAttributePerLine": true
|
||||||
|
}
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:3000",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Nuxt Minimal Starter
|
||||||
|
|
||||||
|
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Make sure to install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Server
|
||||||
|
|
||||||
|
Start the development server on `http://localhost:3000`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
Build the application for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Locally preview production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run preview
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm preview
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn preview
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||||
@@ -2,4 +2,111 @@
|
|||||||
|
|
||||||
RSSA - Front End
|
RSSA - Front End
|
||||||
|
|
||||||
If you see this, the development is still not merged yet. Please check another branch.
|
> [!IMPORTANT]
|
||||||
|
> Read this following instructions before doing your job
|
||||||
|
|
||||||
|
## Framework Guide
|
||||||
|
|
||||||
|
- [Vue Style Guide](https://vuejs.org/style-guide)
|
||||||
|
- [Nuxt Style Guide](https://nuxt.com/docs/4.x/guide)
|
||||||
|
- [Shadcn Vue @radix-ui](https://radix.shadcn-vue.com/)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- `nuxt.config.ts`<br />Nuxt configuration file
|
||||||
|
- `.env`<br />Some environment variables
|
||||||
|
|
||||||
|
## Directory Structure for `app/`
|
||||||
|
|
||||||
|
- `app.vue`: Main layout
|
||||||
|
- `components` : Contains all reusable UI components.
|
||||||
|
- `components/content` : Entry point for business logic and workflows. Pages or routes call these content components to handle API requests and process application logic
|
||||||
|
- `components/app` : View-layer components that manage and present data. These are used within `content/` to render or handle specific parts of the UI, and return results back to the content
|
||||||
|
- `components/pub` : Public/shared components used across different parts of the app.
|
||||||
|
- `composables` : Contains reusable logic and utility functions (e.g. composables, hooks)..
|
||||||
|
- `layouts` : Reusable UI layout patterns used across pages.
|
||||||
|
- `models` : Contains data definitions or interfaces.
|
||||||
|
- `schemas` : Contains JSON schemas used for validation.
|
||||||
|
- `services` : Contains reusable API calls and business logic.
|
||||||
|
|
||||||
|
|
||||||
|
## Directory Structure for `app/pages`
|
||||||
|
|
||||||
|
- `pages/auth` : Authentication related pages.
|
||||||
|
- `pages/(features)` : Grouped feature modules that reflect specific business content or domains.
|
||||||
|
|
||||||
|
## Directory Structure for `server/`
|
||||||
|
|
||||||
|
- `server/api` : API or proxy requests
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
The basic development workflow follows these steps:
|
||||||
|
|
||||||
|
### Define Your Data in `models/`
|
||||||
|
|
||||||
|
- Create data definitions or interfaces.
|
||||||
|
- These should represent the structure of the data used across your app.
|
||||||
|
|
||||||
|
### Build UI Components in `components/app`
|
||||||
|
|
||||||
|
- Create reusable UI and app specific components.
|
||||||
|
- Keep components pure, avoid making HTTP requests directly within them.
|
||||||
|
- They receive data via props and emit events upward.
|
||||||
|
|
||||||
|
### Business Logic in `components/content`
|
||||||
|
|
||||||
|
- This layer connects the UI with the logic (API calls, validations, navigation).
|
||||||
|
- It composes components from `components/app/`, `components/pub/`, and other content.
|
||||||
|
- Also responsible for managing state, side effects, and interactions.
|
||||||
|
|
||||||
|
### Create Pages in `pages/`
|
||||||
|
|
||||||
|
- Define permissions and guards for each page.
|
||||||
|
- Pages load the appropriate content from `components/content/`.
|
||||||
|
- They do not contain UI or logic directly, just route level layout or guards.
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
- UI level validation in `components/app`. help users avoid mistakes before submitting.
|
||||||
|
- Lightweight/instant validation related to UX.
|
||||||
|
- Basic formatting (email regex, numeric-only, password length).
|
||||||
|
- Showing error messages directly under the field.
|
||||||
|
|
||||||
|
- Business level validation in `components/flow`. cannot rely only on the UI, since it often requires server-side checks or rules that may change.
|
||||||
|
- More complex validation rules tied to business logic.
|
||||||
|
|
||||||
|
## Code Conventions
|
||||||
|
|
||||||
|
- Under the script setup block, putting things in group with the following order:
|
||||||
|
- Imports → all dependencies, sorted by external, alias (~), and relative imports.
|
||||||
|
- Props & Emits → clearly define component inputs & outputs.
|
||||||
|
- State & Computed → all ref, reactive, and computed variables grouped together.
|
||||||
|
- Lifecycle Hooks → grouped by mounting → updating → unmounting order.
|
||||||
|
- Functions → async first (fetching, API calls), then utility & event handlers.
|
||||||
|
- Watchers → if needed, put them at the bottom.
|
||||||
|
- Template → keep clean and minimal logic, use methods/computed instead of inline JS.
|
||||||
|
- Declaration Naming
|
||||||
|
- Uses PascalCase for `type` naming
|
||||||
|
- Uses camelCase for `variable` and `const` naming
|
||||||
|
- Underscore can be used to indicates that a variable has derived from an attribute of an object<br />
|
||||||
|
for example: `person_name` indicates it is an attribute `name` from object `person`
|
||||||
|
- Looping
|
||||||
|
- Uses `i`, `j`, `k` as variable for `for` looping index
|
||||||
|
- Uses `item` as object instantition for `forEach` loop callback
|
||||||
|
- Uses `item` as object instantition for `v-for` loop callback in the template
|
||||||
|
|
||||||
|
## Git Workflows
|
||||||
|
|
||||||
|
The basic git workflow follows these steps:
|
||||||
|
|
||||||
|
1. Create a new branch on `dev`
|
||||||
|
- branch name should be `feat/<feature-name>` or `fix/<bug-name>`
|
||||||
|
2. Make your changes
|
||||||
|
3. Commit your changes
|
||||||
|
- commit msg format: `[type]: [description]`
|
||||||
|
- `type` can be `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
|
||||||
|
- `description` should be a brief description of the changes
|
||||||
|
- Example: `feat: add new feature`
|
||||||
|
4. Push your changes to `dev`
|
||||||
|
5. Create a pull request from `dev` to `main`
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ConfigProvider } from 'radix-vue'
|
||||||
|
|
||||||
|
const useIdFunction = () => useId()
|
||||||
|
|
||||||
|
const textDirection = useTextDirection({ initialValue: 'ltr' })
|
||||||
|
const dir = computed(() => (textDirection.value === 'rtl' ? 'rtl' : 'ltr'))
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
// as a string,
|
||||||
|
// where `%s` is replaced with the title
|
||||||
|
titleTemplate: '%s - RSSA',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ConfigProvider :use-id="useIdFunction" :dir="dir">
|
||||||
|
<div vaul-drawer-wrapper class="relative">
|
||||||
|
<NuxtLayout>
|
||||||
|
<NuxtPage />
|
||||||
|
</NuxtLayout>
|
||||||
|
</div>
|
||||||
|
<Toaster />
|
||||||
|
</ConfigProvider>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
/* SIMRS RSSA Design System - Pure CSS (No Tailwind) */
|
||||||
|
|
||||||
|
/* CSS Variables */
|
||||||
|
:root {
|
||||||
|
/* Medical Theme Colors */
|
||||||
|
--background: 230 20% 98%;
|
||||||
|
--foreground: 210 20% 15%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 210 20% 15%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 210 20% 15%;
|
||||||
|
|
||||||
|
/* Primary - Medical Green */
|
||||||
|
--primary: 26 89% 57%;
|
||||||
|
--primary-foreground: 0 0% 100%;
|
||||||
|
--primary-hover: 26, 92%, 65%;
|
||||||
|
|
||||||
|
/* Secondary - Clean Blue */
|
||||||
|
--secondary: 210 50% 96%;
|
||||||
|
--secondary-foreground: 210 20% 20%;
|
||||||
|
--muted: 210 25% 95%;
|
||||||
|
--muted-foreground: 210 15% 50%;
|
||||||
|
|
||||||
|
/* Accent - Neutral Gray */
|
||||||
|
--accent: 210 40% 96%;
|
||||||
|
--accent-foreground: 222.2 84% 4.9%;
|
||||||
|
--destructive: 0 75% 55%;
|
||||||
|
--destructive-foreground: 0 0% 100%;
|
||||||
|
--border: 210 20% 88%;
|
||||||
|
--input: 210 20% 95%;
|
||||||
|
--ring: 150 75% 35%;
|
||||||
|
|
||||||
|
/* Medical Status Colors */
|
||||||
|
--success: 150 75% 40%;
|
||||||
|
--success-foreground: 0 0% 100%;
|
||||||
|
--warning: 45 95% 60%;
|
||||||
|
--warning-foreground: 210 20% 15%;
|
||||||
|
--info: 210 100% 50%;
|
||||||
|
--info-foreground: 0 0% 100%;
|
||||||
|
|
||||||
|
/* Gradients */
|
||||||
|
--gradient-primary: linear-gradient(135deg, hsl(150 75% 35%), hsl(150 75% 45%));
|
||||||
|
--gradient-medical: linear-gradient(135deg, hsl(150 75% 35%), hsl(210 100% 50%));
|
||||||
|
--gradient-subtle: linear-gradient(180deg, hsl(210 20% 98%), hsl(210 25% 95%));
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--shadow-medical: 0 4px 20px -4px hsl(150 75% 35% / 0.15);
|
||||||
|
--shadow-card: 0 2px 10px -2px hsl(210 20% 15% / 0.1);
|
||||||
|
--shadow-glow: 0 0 30px hsl(150 75% 35% / 0.2);
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--transition-fast: all 0.15s ease-out;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
--radius: 0.5rem;
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
--sidebar-background: 0 0% 98%;
|
||||||
|
--sidebar-foreground: 240 5.3% 26.1%;
|
||||||
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
|
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||||
|
--sidebar-border: 220 13% 91%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 210 25% 8%;
|
||||||
|
--foreground: 210 20% 95%;
|
||||||
|
--card: 210 25% 10%;
|
||||||
|
--card-foreground: 210 20% 95%;
|
||||||
|
--popover: 210 25% 10%;
|
||||||
|
--popover-foreground: 210 20% 95%;
|
||||||
|
--primary: 26 89% 57%;
|
||||||
|
--primary-foreground: 0 0% 100%;
|
||||||
|
--primary-hover: 26, 92%, 65%;
|
||||||
|
--secondary: 210 25% 15%;
|
||||||
|
--secondary-foreground: 210 20% 90%;
|
||||||
|
--muted: 210 25% 15%;
|
||||||
|
--muted-foreground: 210 15% 65%;
|
||||||
|
--accent: 210 100% 55%;
|
||||||
|
--accent-foreground: 0 0% 100%;
|
||||||
|
--destructive: 0 75% 60%;
|
||||||
|
--destructive-foreground: 0 0% 100%;
|
||||||
|
--border: 210 25% 20%;
|
||||||
|
--input: 210 25% 15%;
|
||||||
|
--ring: 150 75% 45%;
|
||||||
|
--success: 150 75% 50%;
|
||||||
|
--warning: 45 95% 65%;
|
||||||
|
--info: 210 100% 60%;
|
||||||
|
--gradient-primary: linear-gradient(135deg, hsl(150 75% 45%), hsl(150 75% 55%));
|
||||||
|
--gradient-medical: linear-gradient(135deg, hsl(150 75% 45%), hsl(210 100% 55%));
|
||||||
|
--gradient-subtle: linear-gradient(180deg, hsl(210 25% 8%), hsl(210 25% 12%));
|
||||||
|
--sidebar-background: 240 5.9% 10%;
|
||||||
|
--sidebar-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-primary: 224.3 76.3% 48%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
|
--sidebar-accent: 240 3.7% 15.9%;
|
||||||
|
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-border: 240 3.7% 15.9%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keyframes for Animations */
|
||||||
|
@keyframes accordion-down {
|
||||||
|
from {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: var(--radix-accordion-content-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes accordion-up {
|
||||||
|
from {
|
||||||
|
height: var(--radix-accordion-content-height);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
0% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulseMedical {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 hsl(var(--primary) / 0.4);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 10px hsl(var(--primary) / 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Styles */
|
||||||
|
* {
|
||||||
|
border-color: hsl(var(--border));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* background-color: hsl(var(--background)); */
|
||||||
|
/* background-color: hsl(var(--background), 0.5); */
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-slate-100 dark:bg-slate-800 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, table, label {
|
||||||
|
@apply md:!text-xs xl:!text-sm 2xl:!text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1400px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Color Classes */
|
||||||
|
.bg-background {
|
||||||
|
background-color: hsl(var(--background));
|
||||||
|
}
|
||||||
|
.bg-foreground {
|
||||||
|
background-color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
.bg-card {
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
.bg-primary {
|
||||||
|
background-color: hsl(var(--primary));
|
||||||
|
}
|
||||||
|
.bg-secondary {
|
||||||
|
background-color: hsl(var(--secondary));
|
||||||
|
}
|
||||||
|
.bg-accent {
|
||||||
|
background-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
.bg-destructive {
|
||||||
|
background-color: hsl(var(--destructive));
|
||||||
|
}
|
||||||
|
.bg-muted {
|
||||||
|
background-color: hsl(var(--muted));
|
||||||
|
}
|
||||||
|
.bg-success {
|
||||||
|
background-color: hsl(var(--success));
|
||||||
|
}
|
||||||
|
.bg-warning {
|
||||||
|
background-color: hsl(var(--warning));
|
||||||
|
}
|
||||||
|
.bg-info {
|
||||||
|
background-color: hsl(var(--info));
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-background {
|
||||||
|
color: hsl(var(--background));
|
||||||
|
}
|
||||||
|
.text-foreground {
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
.text-card {
|
||||||
|
color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
.text-primary {
|
||||||
|
color: hsl(var(--primary));
|
||||||
|
}
|
||||||
|
.text-secondary {
|
||||||
|
color: hsl(var(--secondary));
|
||||||
|
}
|
||||||
|
.text-accent {
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
.text-destructive {
|
||||||
|
color: hsl(var(--destructive));
|
||||||
|
}
|
||||||
|
.text-muted {
|
||||||
|
color: hsl(var(--muted));
|
||||||
|
}
|
||||||
|
.text-success {
|
||||||
|
color: hsl(var(--success));
|
||||||
|
}
|
||||||
|
.text-warning {
|
||||||
|
color: hsl(var(--warning));
|
||||||
|
}
|
||||||
|
.text-info {
|
||||||
|
color: hsl(var(--info));
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary-foreground {
|
||||||
|
color: hsl(var(--primary-foreground));
|
||||||
|
}
|
||||||
|
.text-secondary-foreground {
|
||||||
|
color: hsl(var(--secondary-foreground));
|
||||||
|
}
|
||||||
|
.text-accent-foreground {
|
||||||
|
color: hsl(var(--accent-foreground));
|
||||||
|
}
|
||||||
|
.text-destructive-foreground {
|
||||||
|
color: hsl(var(--destructive-foreground));
|
||||||
|
}
|
||||||
|
.text-muted-foreground {
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
.text-success-foreground {
|
||||||
|
color: hsl(var(--success-foreground));
|
||||||
|
}
|
||||||
|
.text-warning-foreground {
|
||||||
|
color: hsl(var(--warning-foreground));
|
||||||
|
}
|
||||||
|
.text-info-foreground {
|
||||||
|
color: hsl(var(--info-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-primary {
|
||||||
|
border-color: hsl(var(--primary));
|
||||||
|
}
|
||||||
|
.border-secondary {
|
||||||
|
border-color: hsl(var(--secondary));
|
||||||
|
}
|
||||||
|
.border-accent {
|
||||||
|
border-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
.border-destructive {
|
||||||
|
border-color: hsl(var(--destructive));
|
||||||
|
}
|
||||||
|
.border-input {
|
||||||
|
border-color: hsl(var(--input));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Colors */
|
||||||
|
.bg-sidebar {
|
||||||
|
background-color: hsl(var(--sidebar-background));
|
||||||
|
}
|
||||||
|
.text-sidebar {
|
||||||
|
color: hsl(var(--sidebar-foreground));
|
||||||
|
}
|
||||||
|
.bg-sidebar-primary {
|
||||||
|
background-color: hsl(var(--sidebar-primary));
|
||||||
|
}
|
||||||
|
.text-sidebar-primary {
|
||||||
|
color: hsl(var(--sidebar-primary));
|
||||||
|
}
|
||||||
|
.bg-sidebar-accent {
|
||||||
|
background-color: hsl(var(--sidebar-accent));
|
||||||
|
}
|
||||||
|
.text-sidebar-accent {
|
||||||
|
color: hsl(var(--sidebar-accent));
|
||||||
|
}
|
||||||
|
.border-sidebar {
|
||||||
|
border-color: hsl(var(--sidebar-border));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background Images */
|
||||||
|
.bg-gradient-medical {
|
||||||
|
background-image: var(--gradient-medical);
|
||||||
|
}
|
||||||
|
.bg-gradient-primary {
|
||||||
|
background-image: var(--gradient-primary);
|
||||||
|
}
|
||||||
|
.bg-gradient-subtle {
|
||||||
|
background-image: var(--gradient-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
.rounded-sm {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Error Styling */
|
||||||
|
.field-error-info {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
color: hsl(var(--destructive));
|
||||||
|
/* font-size: 0.875rem; */
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
min-height: 1rem; /* Reserve space to prevent CLS */
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .rounded-md { border-radius: var */
|
||||||
|
|
||||||
|
/* Dashboard grid utility */
|
||||||
|
.dashboard-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/form/field.vue'
|
||||||
|
import Label from '~/components/pub/form/label.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Icon name="i-lucide-user" class="me-2" />
|
||||||
|
<span class="font-semibold">Tambah</span> Pasien
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nomor RM</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label dynamic>Alamat</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<PubNavFooterCsd />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 120 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{ width: 50 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'Rekam Medis' },
|
||||||
|
{ label: 'KTP' },
|
||||||
|
{ label: 'Tgl Lahir' },
|
||||||
|
{ label: 'Umur' },
|
||||||
|
{ label: 'JK' },
|
||||||
|
{ label: 'Pendidikan' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'name',
|
||||||
|
'medicalRecord_number',
|
||||||
|
'identity_number',
|
||||||
|
'birth_date',
|
||||||
|
'patient_age',
|
||||||
|
'gender',
|
||||||
|
'education',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
birth_date: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.birth_date == 'object' && recX.birth_date) {
|
||||||
|
return (recX.birth_date as Date).toLocaleDateString()
|
||||||
|
} else if (typeof recX.birth_date == 'string') {
|
||||||
|
return (recX.birth_date as string).substring(0, 10)
|
||||||
|
}
|
||||||
|
return recX.birth_date
|
||||||
|
},
|
||||||
|
patient_age: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.birth_date?.split('T')[0]
|
||||||
|
},
|
||||||
|
gender: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX?.gender_code !== 'number' && recX?.gender_code !== '') {
|
||||||
|
return 'Tidak Diketahui'
|
||||||
|
}
|
||||||
|
return recX.gender_code
|
||||||
|
},
|
||||||
|
education: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.education_code == 'number' && recX.education_code >= 0) {
|
||||||
|
return recX.education_code
|
||||||
|
} else if (typeof recX.education_code) {
|
||||||
|
return recX.education_code
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { z } from 'zod'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Loader2 } from 'lucide-vue-next'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
isLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [data: any]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { handleSubmit, defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [password, passwordAttrs] = defineField('password')
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit(async (values) => {
|
||||||
|
try {
|
||||||
|
await emit('submit', values)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Submission failed:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="grid gap-6" @submit="onSubmit">
|
||||||
|
<div class="grid gap-2">
|
||||||
|
<Label for="name">Username</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
v-model="name"
|
||||||
|
v-bind="nameAttrs"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:class="{ 'border-red-500': errors.name }"
|
||||||
|
/>
|
||||||
|
<span v-if="errors.name" class="text-sm text-red-500">
|
||||||
|
{{ errors.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-2">
|
||||||
|
<Label for="password">Password</Label>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
v-model="password"
|
||||||
|
v-bind="passwordAttrs"
|
||||||
|
type="password"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:class="{ 'border-red-500': errors.password }"
|
||||||
|
/>
|
||||||
|
<span v-if="errors.password" class="text-sm text-red-500">
|
||||||
|
{{ errors.password }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" class="w-full" :disabled="isLoading || !meta.valid">
|
||||||
|
<Loader2 v-if="isLoading" class="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InfraFormData } from '~/schemas/infra.schema'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InfraFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
infraGroup_code: 'counter',
|
||||||
|
parent_id: null,
|
||||||
|
} as Partial<InfraFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [infraGroup_code, infraGroupAttrs] = defineField('infraGroup_code')
|
||||||
|
const [parent_id, parentIdAttrs] = defineField('parent_id')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
|
||||||
|
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
infraGroup_code.value = 'counter'
|
||||||
|
parent_id.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: InfraFormData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
infraGroup_code: infraGroup_code.value || 'counter',
|
||||||
|
parent_id: parent_id.value || null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-counter" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{ width: 100 }, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Counter Induk' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'parent', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
parent: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.parent?.name || '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
props: {
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubBaseDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { infraGroupCodesKeys } from "~/lib/constants"
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InfraFormData } from '~/schemas/infra.schema'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InfraFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
infraGroup_code: infraGroupCodesKeys.building,
|
||||||
|
parent_id: null,
|
||||||
|
} as Partial<InfraFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [infraGroup_code] = defineField('infraGroup_code')
|
||||||
|
const [parent_id] = defineField('parent_id')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
|
||||||
|
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
infraGroup_code.value = infraGroupCodesKeys.building
|
||||||
|
parent_id.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: InfraFormData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.building,
|
||||||
|
parent_id: parent_id.value || null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-building" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InfraFormData } from '~/schemas/infra.schema'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InfraFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
infraGroup_code: 'counter',
|
||||||
|
parent_id: null,
|
||||||
|
} as Partial<InfraFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [infraGroup_code, infraGroupAttrs] = defineField('infraGroup_code')
|
||||||
|
const [parent_id, parentIdAttrs] = defineField('parent_id')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
|
||||||
|
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
infraGroup_code.value = 'counter'
|
||||||
|
parent_id.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: InfraFormData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
infraGroup_code: infraGroup_code.value || 'counter',
|
||||||
|
parent_id: parent_id.value || null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-counter" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{ width: 100 }, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Counter Induk' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'parent', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
parent: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.parent?.name || '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
props: {
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubBaseDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { infraGroupCodesKeys } from "~/lib/constants"
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InfraFormData } from '~/schemas/infra.schema'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InfraFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
infraGroup_code: infraGroupCodesKeys.building,
|
||||||
|
parent_id: null,
|
||||||
|
} as Partial<InfraFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [infraGroup_code] = defineField('infraGroup_code')
|
||||||
|
const [parent_id] = defineField('parent_id')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
|
||||||
|
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
infraGroup_code.value = infraGroupCodesKeys.building
|
||||||
|
parent_id.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: InfraFormData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.building,
|
||||||
|
parent_id: parent_id.value || null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-building" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import TreeSelect from '~/components/pub/my-ui/select-tree/tree-select.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { DivisionFormData } from '~/schemas/division.schema.ts'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
divisions: any[]
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: DivisionFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
parent_id: null,
|
||||||
|
division_id: null,
|
||||||
|
} as Partial<DivisionFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [parent, parentAttrs] = defineField('parent_id')
|
||||||
|
const [division] = defineField('division_id')
|
||||||
|
|
||||||
|
// Fill fields from props.values if provided
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.parent_id !== undefined) parent.value = String(props.values.parent_id)
|
||||||
|
if (props.values.division_id !== undefined) division.value = String(props.values.division_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
parent.value = null
|
||||||
|
division.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: DivisionFormData = {
|
||||||
|
name: name.value || '',
|
||||||
|
code: code.value || '',
|
||||||
|
parent_id: parent.value || null,
|
||||||
|
division_id: division.value || null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form cancel handler
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-division" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Divisi Induk</Label>
|
||||||
|
<Field :errMessage="errors.division">
|
||||||
|
<TreeSelect
|
||||||
|
id="division"
|
||||||
|
v-model="parent"
|
||||||
|
v-bind="parentAttrs"
|
||||||
|
:data="divisions"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
placeholder="Pilih Divisi Induk"
|
||||||
|
search-placeholder="Cari divisi"
|
||||||
|
empty-message="Divisi tidak ditemukan"
|
||||||
|
:on-fetch-children="async (_parentId: string) => {}"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Divisi Induk' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'parent', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
parent: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.parent?.name || '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
props: {
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ value: 'doctor', label: 'Dokter' },
|
||||||
|
{ value: 'nurse', label: 'Perawat' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-2 border-b border-b-slate-300 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>No. IHS</Label>
|
||||||
|
<Field>
|
||||||
|
<Input />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>No. SIP</Label>
|
||||||
|
<Field>
|
||||||
|
<Input />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>Unit</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih jenis" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
|
const _doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 250 },
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 120 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Kode JKN' },
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'No KTP' },
|
||||||
|
{ label: 'No SIP' },
|
||||||
|
{ label: 'No IHS' },
|
||||||
|
{ label: 'Telpon' },
|
||||||
|
{ label: 'Fee Ranap' },
|
||||||
|
{ label: 'Fee Rajal' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'bpjs_code',
|
||||||
|
'name',
|
||||||
|
'identity_number',
|
||||||
|
'sip_no',
|
||||||
|
'ihs_number',
|
||||||
|
'phone',
|
||||||
|
'inPatient_itemPrice',
|
||||||
|
'outPatient_itemPrice',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.frontTitle} ${recX.name} ${recX.endTitle}`.trim()
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
inPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.inPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
outPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.outPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any; items: any[] }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>Nomor Induk Pegawai</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.number" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>Status</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih jenis" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>Position</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih jenis" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label>Divisi</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih jenis" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 250 },
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 50 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Kode JKN' },
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'No KTP' },
|
||||||
|
{ label: 'No SIP' },
|
||||||
|
{ label: 'No IHS' },
|
||||||
|
{ label: 'Telpon' },
|
||||||
|
{ label: 'Fee Ranap' },
|
||||||
|
{ label: 'Fee Rajal' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'bpjs_code',
|
||||||
|
'name',
|
||||||
|
'identity_number',
|
||||||
|
'sip_no',
|
||||||
|
'ihs_number',
|
||||||
|
'phone',
|
||||||
|
'inPatient_itemPrice',
|
||||||
|
'outPatient_itemPrice',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
console.log(rec)
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.frontTitle} ${recX.name} ${recX.endTitle}`.trim()
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
inPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.inPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
outPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.outPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
status: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return doctorStatus[recX.status_code as keyof typeof doctorStatus]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import DatepickerSingle from '~/components/pub/my-ui/form/datepicker-single.vue'
|
||||||
|
|
||||||
|
import { educationCodes, genderCodes, occupationCodes, religionCodes, relationshipCodes } from '~/lib/constants'
|
||||||
|
import { mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
|
||||||
|
interface DivisionFormData {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
parentId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
division: {
|
||||||
|
msg: {
|
||||||
|
placeholder: string
|
||||||
|
search: string
|
||||||
|
empty: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items: {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
code: string
|
||||||
|
}[]
|
||||||
|
schema: any
|
||||||
|
initialValues?: Partial<DivisionFormData>
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: DivisionFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
click: (e: Event) => void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const relationshipOpts = mapToComboboxOptList(relationshipCodes)
|
||||||
|
const educationOpts = mapToComboboxOptList(educationCodes)
|
||||||
|
const occupationOpts = mapToComboboxOptList(occupationCodes)
|
||||||
|
const genderOpts = mapToComboboxOptList(genderCodes)
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||||
|
const formData: DivisionFormData = {
|
||||||
|
name: values.name || '',
|
||||||
|
code: values.code || '',
|
||||||
|
parentId: values.parentId || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
const doctorOpts = ref([
|
||||||
|
{ label: 'Pilih', value: null },
|
||||||
|
{ label: 'Dr. A', value: 1 },
|
||||||
|
])
|
||||||
|
const paymentOpts = ref([
|
||||||
|
{ label: 'Umum', value: 'umum' },
|
||||||
|
{ label: 'BPJS', value: 'bpjs' },
|
||||||
|
])
|
||||||
|
const sepOpts = ref([
|
||||||
|
{ label: 'Rujukan Internal', value: 'ri' },
|
||||||
|
{ label: 'SEP Rujukan', value: 'sr' },
|
||||||
|
])
|
||||||
|
|
||||||
|
// file refs untuk tombol "Pilih Berkas"
|
||||||
|
const sepFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const sippFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
|
function pickSepFile() {
|
||||||
|
sepFileInput.value?.click()
|
||||||
|
}
|
||||||
|
function pickSippFile() {
|
||||||
|
sippFileInput.value?.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSepFileChange(e: Event) {
|
||||||
|
const f = (e.target as HTMLInputElement).files?.[0]
|
||||||
|
// set ke form / emit / simpan di state sesuai form library-mu
|
||||||
|
console.log('sep file', f)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSippFileChange(e: Event) {
|
||||||
|
const f = (e.target as HTMLInputElement).files?.[0]
|
||||||
|
console.log('sipp file', f)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddSep() {
|
||||||
|
// contoh handler tombol "+" di sebelah No. SEP
|
||||||
|
console.log('open modal tambah SEP')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
v-slot="{ handleSubmit, resetForm }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
:initial-values="initialValues"
|
||||||
|
>
|
||||||
|
<form id="entry-form" @submit="handleSubmit($event, (values) => onSubmitForm(values, { resetForm }))">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<div class="p-2">
|
||||||
|
<h2 class="text-md font-semibold">Data Pasien</h2>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 flex gap-6 p-2 text-sm">
|
||||||
|
<span>
|
||||||
|
Sudah pernah terdaftar sebagai pasien?
|
||||||
|
<Button class="bg-primary" size="sm" @click.prevent="emit('click', 'search')">
|
||||||
|
<Icon name="i-lucide-search" class="mr-1" /> Cari Pasien
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Belum pernah terdaftar sebagai pasien?
|
||||||
|
<Button class="bg-primary" size="sm" @click.prevent="emit('click', 'add')">
|
||||||
|
<Icon name="i-lucide-plus" class="mr-1" /> Tambah Pasien Baru
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Block :colCount="3">
|
||||||
|
<Cell>
|
||||||
|
<Label label-for="patient_name">Nama Pasien</Label>
|
||||||
|
<Field id="patient_name" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="patient_name">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="patient_name"
|
||||||
|
v-bind="componentField"
|
||||||
|
disabled
|
||||||
|
placeholder="Tambah data pasien terlebih dahulu"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- NIK -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="nik">NIK</Label>
|
||||||
|
<Field id="nik" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="nik">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input id="nik" v-bind="componentField" disabled placeholder="Otomatis" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label label-for="rm">No. RM</Label>
|
||||||
|
<Field id="rm" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="rm">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input id="rm" v-bind="componentField" disabled placeholder="RM99222" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<div class="p-2">
|
||||||
|
<h2 class="text-md font-semibold">Data Kunjungan</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Block :colCount="3">
|
||||||
|
<!-- Dokter (Combobox) -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="doctor_id">Dokter</Label>
|
||||||
|
<Field id="doctor_id" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="doctor_id">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox id="doctor_id" v-bind="componentField" :items="doctorOpts" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- Tanggal Daftar (DatePicker) -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="register_date">Tanggal Daftar</Label>
|
||||||
|
<Field id="register_date" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="register_date">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<DatepickerSingle v-bind="componentField" placeholder="Pilih tanggal" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- Jenis Pembayaran (Combobox) -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="payment_type">Jenis Pembayaran</Label>
|
||||||
|
<Field id="payment_type" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="payment_type">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<!-- <Combobox id="payment_type" v-bind="componentField" :items="paymentOpts" /> -->
|
||||||
|
<Select id="payment_type" v-bind="componentField" :items="paymentOpts" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
|
||||||
|
<Block :colCount="3">
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="bpjs_number">Kelompok Peserta</Label>
|
||||||
|
<Field id="bpjs_number" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="bpjs_number">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="bpjs_number"
|
||||||
|
v-bind="componentField"
|
||||||
|
placeholder="Pilih jenis pembayaran terlebih dahulu"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- No. Kartu BPJS -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="bpjs_number">No. Kartu BPJS</Label>
|
||||||
|
<Field id="bpjs_number" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="bpjs_number">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="bpjs_number"
|
||||||
|
v-bind="componentField"
|
||||||
|
placeholder="Pilih jenis pembayaran terlebih dahulu"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- Jenis SEP -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="sep_type">Jenis SEP</Label>
|
||||||
|
<Field id="sep_type" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="sep_type">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Select id="sep_type" v-bind="componentField" :items="sepOpts" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
|
||||||
|
<Block :colCount="3">
|
||||||
|
<!-- No. SEP (input + tombol +) -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="sep_number">No. SEP</Label>
|
||||||
|
<Field id="sep_number" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="sep_number">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<Input
|
||||||
|
id="sep_number"
|
||||||
|
v-bind="componentField"
|
||||||
|
placeholder="Tambah SEP terlebih dahulu"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
<Button class="bg-primary" size="sm" variant="outline" @click.prevent="onAddSep">+</Button>
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- Dokumen SEP (file) -->
|
||||||
|
<Cell :cosSpan="3">
|
||||||
|
<Label label-for="sep_file">Dokumen SEP</Label>
|
||||||
|
<Field id="sep_file" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="sep_file">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input ref="sepFileInput" type="file" class="hidden" @change="onSepFileChange" />
|
||||||
|
<Button class="bg-primary" size="sm" variant="ghost" @click.prevent="pickSepFile"
|
||||||
|
>Pilih Berkas</Button
|
||||||
|
>
|
||||||
|
<Input readonly v-bind="componentField" placeholder="Unggah dokumen SEP" />
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<!-- Dokumen SIPP (file) -->
|
||||||
|
<Cell :cosSpan="3" labelSize="thin">
|
||||||
|
<Label label-for="sipp_file">Dokumen SIPP</Label>
|
||||||
|
<Field id="sipp_file" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="sipp_file">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input ref="sippFileInput" type="file" class="hidden" @change="onSippFileChange" />
|
||||||
|
<Button class="bg-primary" size="sm" variant="ghost" @click.prevent="pickSippFile"
|
||||||
|
>Pilih Berkas</Button
|
||||||
|
>
|
||||||
|
<Input readonly v-bind="componentField" placeholder="Unggah dokumen SIPP" />
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
|
||||||
|
interface InstallationFormData {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
encounterClassCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
installation: {
|
||||||
|
msg: {
|
||||||
|
placeholder: string
|
||||||
|
}
|
||||||
|
items: {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
code: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
schema: any
|
||||||
|
initialValues?: Partial<InstallationFormData>
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InstallationFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||||
|
const formData: InstallationFormData = {
|
||||||
|
name: values.name || '',
|
||||||
|
code: values.code || '',
|
||||||
|
encounterClassCode: values.encounterClassCode || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form cancel handler
|
||||||
|
function onCancelForm({ resetForm }: { resetForm: () => void }) {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = ref([
|
||||||
|
{ label: 'Rujukan Internal', value: 'ri' },
|
||||||
|
{ label: 'SEP Rujukan', value: 'sr' },
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
v-slot="{ handleSubmit, resetForm }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
:initial-values="initialValues"
|
||||||
|
>
|
||||||
|
<form id="entry-form" @submit="handleSubmit($event, (values) => onSubmitForm(values, { resetForm }))">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<FieldGroup>
|
||||||
|
<Label label-for="parentId">Cara Bayar</Label>
|
||||||
|
<Field id="encounterClassCode" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="encounterClassCode">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Select v-bind="componentField" :items="items" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label label-for="parentId">Poliklinik</Label>
|
||||||
|
<Field id="encounterClassCode" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="encounterClassCode">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Select v-bind="componentField" :items="items" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label label-for="parentId">Kunjungan</Label>
|
||||||
|
<Field id="encounterClassCode" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="encounterClassCode">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Select v-bind="componentField" :items="items" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-pdud.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 120 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{ width: 50 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'Rekam Medis' },
|
||||||
|
{ label: 'KTP' },
|
||||||
|
{ label: 'Tgl Lahir' },
|
||||||
|
{ label: 'Umur' },
|
||||||
|
{ label: 'JK' },
|
||||||
|
{ label: 'Pendidikan' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'name',
|
||||||
|
'medicalRecord_number',
|
||||||
|
'identity_number',
|
||||||
|
'birth_date',
|
||||||
|
'patient_age',
|
||||||
|
'gender',
|
||||||
|
'education',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
birth_date: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.birth_date == 'object' && recX.birth_date) {
|
||||||
|
return (recX.birth_date as Date).toLocaleDateString()
|
||||||
|
} else if (typeof recX.birth_date == 'string') {
|
||||||
|
return (recX.birth_date as string).substring(0, 10)
|
||||||
|
}
|
||||||
|
return recX.birth_date
|
||||||
|
},
|
||||||
|
patient_age: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.birth_date?.split('T')[0]
|
||||||
|
},
|
||||||
|
gender: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX?.gender_code !== 'number' && recX?.gender_code !== '') {
|
||||||
|
return 'Tidak Diketahui'
|
||||||
|
}
|
||||||
|
return recX.gender_code
|
||||||
|
},
|
||||||
|
education: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.education_code == 'number' && recX.education_code >= 0) {
|
||||||
|
return recX.education_code
|
||||||
|
} else if (typeof recX.education_code) {
|
||||||
|
return recX.education_code
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
if (rec.status === null) {
|
||||||
|
rec.status_code = 0
|
||||||
|
}
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full rounded-md border bg-white p-4 shadow-sm">
|
||||||
|
<!-- Data Pasien -->
|
||||||
|
<h2 class="mb-2 md:text-base 2xl:text-lg font-semibold">{{ 'data.patient.person.name' }} - {{ 'data.patient.number' }}</h2>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3" >
|
||||||
|
<div>
|
||||||
|
<DE.Block mode="preview" labelSize="large">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Tgl. Lahir</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.patient.person.birthDate' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Jenis Kelamin</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.patient.person.gender_code' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Alamat</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
<div v-html="'data.patient.person .addresses[0].address'"></div>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DE.Block mode="preview" labelSize="large">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Tgl. Kunjungan</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.date' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Klinik</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.unit.name' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>DPJP</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.doctor.name' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DE.Block mode="preview" labelSize="large">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Tgl. Kunjungan</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.date' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Klinik</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.unit.name' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>DPJP</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ 'data.doctor.name' }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">Hello World!!!</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { MaterialFormData } from '~/schemas/material.schema.ts'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
uoms: any[]
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: MaterialFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
uom_code: '',
|
||||||
|
stock: 0,
|
||||||
|
} as Partial<MaterialFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [uom, uomAttrs] = defineField('uom_code')
|
||||||
|
const [stock, stockAttrs] = defineField('stock')
|
||||||
|
|
||||||
|
// Fill fields from props.values if provided
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.uom_code !== undefined) uom.value = props.values.uom_code
|
||||||
|
if (props.values.stock !== undefined) stock.value = props.values.stock
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
uom.value = ''
|
||||||
|
stock.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: MaterialFormData = {
|
||||||
|
name: name.value || '',
|
||||||
|
code: code.value || '',
|
||||||
|
uom_code: uom.value || '',
|
||||||
|
stock: stock.value || 0,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-equipment" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Satuan</Label>
|
||||||
|
<Field :errMessage="errors.uom_code">
|
||||||
|
<Select
|
||||||
|
id="uom"
|
||||||
|
v-model="uom"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Pilih satuan"
|
||||||
|
v-bind="uomAttrs"
|
||||||
|
:items="uoms"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Stok</Label>
|
||||||
|
<Field :errMessage="errors.stock">
|
||||||
|
<Input id="stock" v-model="stock" type="number" v-bind="stockAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { Col, KeyLabel, RecComponent, RecStrFuncComponent, Th } from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{ width: 100 }, { width: 250 }, { width: 100 }, { width: 100 }, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Stok' }, { label: 'Satuan' }, { label: '' }],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'stock', 'uom_code', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: Record<string, (row: any, ...args: any[]) => any> = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.name}`.trim()
|
||||||
|
},
|
||||||
|
uom_code: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.uom_code
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: Record<string, (row: any, ...args: any[]) => any> = {}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { infraGroupCodesKeys } from "~/lib/constants"
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InfraFormData } from '~/schemas/infra.schema'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
parents: any[]
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InfraFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
infraGroup_code: infraGroupCodesKeys.floor,
|
||||||
|
parent_id: null,
|
||||||
|
} as Partial<InfraFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [infraGroup_code] = defineField('infraGroup_code')
|
||||||
|
const [parent_id, parentIdAttrs] = defineField('parent_id')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
|
||||||
|
if (props.values.parent_id !== undefined) parent_id.value = String(props.values.parent_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
infraGroup_code.value = infraGroupCodesKeys.floor
|
||||||
|
parent_id.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData: InfraFormData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.floor,
|
||||||
|
parent_id: parent_id.value ? Number(parent_id.value) : null,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-floor" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Gedung</Label>
|
||||||
|
<Field :errMessage="errors.parent_id">
|
||||||
|
<Combobox
|
||||||
|
id="parent"
|
||||||
|
v-model="parent_id"
|
||||||
|
v-bind="parentIdAttrs"
|
||||||
|
:items="parents"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
placeholder="Pilih Gedung"
|
||||||
|
search-placeholder="Cari Gedung"
|
||||||
|
empty-message="Item tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Gedung' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'parent', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
parent: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.parent?.name || '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label>Nomor RM</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label dynamic>Alamat</Label>
|
||||||
|
<Field>
|
||||||
|
<Input type="text" name="name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<Action />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 120 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{ width: 50 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'Rekam Medis' },
|
||||||
|
{ label: 'KTP' },
|
||||||
|
{ label: 'Tgl Lahir' },
|
||||||
|
{ label: 'Umur' },
|
||||||
|
{ label: 'JK' },
|
||||||
|
{ label: 'Pendidikan' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'name',
|
||||||
|
'medicalRecord_number',
|
||||||
|
'identity_number',
|
||||||
|
'birth_date',
|
||||||
|
'patient_age',
|
||||||
|
'gender',
|
||||||
|
'education',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
birth_date: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.birth_date == 'object' && recX.birth_date) {
|
||||||
|
return (recX.birth_date as Date).toLocaleDateString()
|
||||||
|
} else if (typeof recX.birth_date == 'string') {
|
||||||
|
return (recX.birth_date as string).substring(0, 10)
|
||||||
|
}
|
||||||
|
return recX.birth_date
|
||||||
|
},
|
||||||
|
patient_age: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.birth_date?.split('T')[0]
|
||||||
|
},
|
||||||
|
gender: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX?.gender_code !== 'number' && recX?.gender_code !== '') {
|
||||||
|
return 'Tidak Diketahui'
|
||||||
|
}
|
||||||
|
return recX.gender_code
|
||||||
|
},
|
||||||
|
education: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (typeof recX.education_code == 'number' && recX.education_code >= 0) {
|
||||||
|
return recX.education_code
|
||||||
|
} else if (typeof recX.education_code) {
|
||||||
|
return recX.education_code
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
if (rec.status === null) {
|
||||||
|
rec.status_code = 0
|
||||||
|
}
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{ data: any[] }>()
|
||||||
|
const modelValue = defineModel<any | null>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-model="modelValue"
|
||||||
|
select-mode="multi"
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Trash2 } from 'lucide-vue-next'
|
||||||
|
// import { Button } from '@/components/ui/button'
|
||||||
|
// import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||||
|
|
||||||
|
interface Diagnosa {
|
||||||
|
id: number
|
||||||
|
diagnosa: string
|
||||||
|
icd: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = ref<Diagnosa[]>([{ id: 1, diagnosa: 'Acute appendicitis', icd: 'K35' }])
|
||||||
|
|
||||||
|
function removeItem(id: number) {
|
||||||
|
list.value = list.value.filter((item) => item.id !== id)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rounded-lg border bg-white shadow-sm">
|
||||||
|
<Table>
|
||||||
|
<TableHeader class="bg-gray-50">
|
||||||
|
<TableRow>
|
||||||
|
<TableHead class="w-[50px] text-center">NO.</TableHead>
|
||||||
|
<TableHead>DIAGNOSA</TableHead>
|
||||||
|
<TableHead class="w-[120px]">ICD-X</TableHead>
|
||||||
|
<TableHead class="w-[80px] text-center">AKSI</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
<TableRow v-for="(item, i) in list" :key="item.id">
|
||||||
|
<TableCell class="text-center font-medium">{{ i + 1 }}</TableCell>
|
||||||
|
<TableCell>{{ item.diagnosa }}</TableCell>
|
||||||
|
<TableCell>{{ item.icd }}</TableCell>
|
||||||
|
<TableCell class="text-center">
|
||||||
|
<Button variant="ghost" size="icon" @click="removeItem(item.id)">
|
||||||
|
<Trash2 class="h-4 w-4 text-gray-500 hover:text-red-500" />
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { InstallationFormData } from '~/schemas/installation.schema.ts'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
encounterClasses: any[]
|
||||||
|
values: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InstallationFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
encounterClass_code: '',
|
||||||
|
} as Partial<InstallationFormData>,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [encounterClassCode, encounterClassCodeAttrs] = defineField('encounterClass_code')
|
||||||
|
|
||||||
|
// Fill fields from props.values if provided
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.encounterClass_code !== undefined) encounterClassCode.value = props.values.encounterClass_code
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
encounterClassCode.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any) {
|
||||||
|
const formData: InstallationFormData = {
|
||||||
|
name: name.value || '',
|
||||||
|
code: code.value || '',
|
||||||
|
encounterClass_code: encounterClassCode.value || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form cancel handler
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-unit" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Encounter Class</Label>
|
||||||
|
<Field :errMessage="errors.encounterClass_code">
|
||||||
|
<Combobox
|
||||||
|
id="encounterClass_code"
|
||||||
|
v-model="encounterClassCode"
|
||||||
|
v-bind="encounterClassCodeAttrs"
|
||||||
|
:items="encounterClasses"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
placeholder="Pilih Encounter Class"
|
||||||
|
search-placeholder="Cari Encounter Class"
|
||||||
|
empty-message="Encounter tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Encounter Class' }, { label: '' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'encounterClass_code', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.name}`.trim()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
props: {
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ value: '1', label: 'item 1' },
|
||||||
|
{ value: '2', label: 'item 2' },
|
||||||
|
{ value: '3', label: 'item 3' },
|
||||||
|
{ value: '4', label: 'item 4' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Items</Label>
|
||||||
|
<Field>
|
||||||
|
<Select :items="items" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Perusahaan Insuransi</Label>
|
||||||
|
<Field>
|
||||||
|
<Select :items="items" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Harga</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.price" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
const _doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Aksi' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ value: '1', label: 'item 1' },
|
||||||
|
{ value: '2', label: 'item 2' },
|
||||||
|
{ value: '3', label: 'item 3' },
|
||||||
|
{ value: '4', label: 'item 4' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Kode</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.code" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Item Group</Label>
|
||||||
|
<Field>
|
||||||
|
<Select :items="items" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>UOM</Label>
|
||||||
|
<Field>
|
||||||
|
<Select :items="items" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Infra</Label>
|
||||||
|
<Field>
|
||||||
|
<Select :items="items" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>Harga</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.price" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
const _doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Aksi' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-2 border-b border-b-slate-300 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>No. IHS</Label>
|
||||||
|
<Field>
|
||||||
|
<Input />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
|
const _doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 250 },
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 120 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Kode JKN' },
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'No KTP' },
|
||||||
|
{ label: 'No SIP' },
|
||||||
|
{ label: 'No IHS' },
|
||||||
|
{ label: 'Telpon' },
|
||||||
|
{ label: 'Fee Ranap' },
|
||||||
|
{ label: 'Fee Rajal' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'bpjs_code',
|
||||||
|
'name',
|
||||||
|
'identity_number',
|
||||||
|
'sip_no',
|
||||||
|
'ihs_number',
|
||||||
|
'phone',
|
||||||
|
'inPatient_itemPrice',
|
||||||
|
'outPatient_itemPrice',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.frontTitle} ${recX.name} ${recX.endTitle}`.trim()
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
inPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.inPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
outPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.outPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { BaseFormData } from '~/schemas/base.schema.ts'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema?: z.ZodSchema<any>
|
||||||
|
values?: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: BaseFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: props.schema ? toTypedSchema(props.schema) : undefined,
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData = {
|
||||||
|
name: name.value || '',
|
||||||
|
code: code.value || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-medicine-group" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Aksi' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { BaseFormData } from '~/schemas/base.schema.ts'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema?: z.ZodSchema<any>
|
||||||
|
values?: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: BaseFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: props.schema ? toTypedSchema(props.schema) : undefined,
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData = {
|
||||||
|
name: name.value || '',
|
||||||
|
code: code.value || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-medicine-method" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Aksi' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||||
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
schema?: z.ZodSchema<any>
|
||||||
|
values?: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
medicineGroups?: { value: string; label: string }[]
|
||||||
|
medicineMethods?: { value: string; label: string }[]
|
||||||
|
uoms?: { value: string; label: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: any, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { defineField, errors, meta } = useForm({
|
||||||
|
validationSchema: props.schema ? toTypedSchema(props.schema) : undefined,
|
||||||
|
initialValues: {
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
medicineGroup_code: '',
|
||||||
|
medicineMethod_code: '',
|
||||||
|
uom_code: '',
|
||||||
|
stock: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [code, codeAttrs] = defineField('code')
|
||||||
|
const [name, nameAttrs] = defineField('name')
|
||||||
|
const [medicineGroup_code, medicineGroupAttrs] = defineField('medicineGroup_code')
|
||||||
|
const [medicineMethod_code, medicineMethodAttrs] = defineField('medicineMethod_code')
|
||||||
|
const [uom_code, uomAttrs] = defineField('uom_code')
|
||||||
|
const [stock, stockAttrs] = defineField('stock')
|
||||||
|
|
||||||
|
if (props.values) {
|
||||||
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
|
if (props.values.medicineGroup_code !== undefined) medicineGroup_code.value = props.values.medicineGroup_code
|
||||||
|
if (props.values.medicineMethod_code !== undefined) medicineMethod_code.value = props.values.medicineMethod_code
|
||||||
|
if (props.values.uom_code !== undefined) uom_code.value = props.values.uom_code
|
||||||
|
if (props.values.stock !== undefined) stock.value = props.values.stock
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
code.value = ''
|
||||||
|
name.value = ''
|
||||||
|
medicineGroup_code.value = ''
|
||||||
|
medicineMethod_code.value = ''
|
||||||
|
uom_code.value = ''
|
||||||
|
stock.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmitForm() {
|
||||||
|
const formData = {
|
||||||
|
code: code.value || '',
|
||||||
|
name: name.value || '',
|
||||||
|
medicineGroup_code: medicineGroup_code.value || '',
|
||||||
|
medicineMethod_code: medicineMethod_code.value || '',
|
||||||
|
uom_code: uom_code.value || '',
|
||||||
|
stock: stock.value || 0,
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancelForm() {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="form-medicine" @submit.prevent>
|
||||||
|
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
|
||||||
|
<Cell>
|
||||||
|
<Label height="">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input
|
||||||
|
id="code"
|
||||||
|
v-model="code"
|
||||||
|
v-bind="codeAttrs"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
v-model="name"
|
||||||
|
v-bind="nameAttrs"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Kelompok Obat</Label>
|
||||||
|
<Field :errMessage="errors.medicineGroup_code">
|
||||||
|
<Select
|
||||||
|
id="medicineGroup_code"
|
||||||
|
v-model="medicineGroup_code"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Pilih kelompok obat"
|
||||||
|
v-bind="medicineGroupAttrs"
|
||||||
|
:items="props.medicineGroups || []"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Metode Pemberian</Label>
|
||||||
|
<Field :errMessage="errors.medicineMethod_code">
|
||||||
|
<Select
|
||||||
|
id="medicineMethod_code"
|
||||||
|
v-model="medicineMethod_code"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Pilih metode pemberian"
|
||||||
|
v-bind="medicineMethodAttrs"
|
||||||
|
:items="props.medicineMethods || []"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Satuan</Label>
|
||||||
|
<Field :errMessage="errors.uom_code">
|
||||||
|
<Select
|
||||||
|
id="uom_code"
|
||||||
|
v-model="uom_code"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Pilih satuan"
|
||||||
|
v-bind="uomAttrs"
|
||||||
|
:items="props.uoms || []"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<!-- <Cell>
|
||||||
|
<Label height="compact">Infra</Label>
|
||||||
|
<Field :errMessage="errors.infra_id">
|
||||||
|
<Select
|
||||||
|
id="infra_id"
|
||||||
|
v-model="infra_id"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Tidak ada"
|
||||||
|
v-bind="infraAttrs"
|
||||||
|
:items="props.infras || []"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell> -->
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Stok</Label>
|
||||||
|
<Field :errMessage="errors.stock">
|
||||||
|
<Input
|
||||||
|
id="stock"
|
||||||
|
v-model="stock"
|
||||||
|
type="number"
|
||||||
|
v-bind="stockAttrs"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
|
||||||
|
export const cols: Col[] = [{}, {}, {}, {}, {}, {}, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Kode' },
|
||||||
|
{ label: 'Name' },
|
||||||
|
{ label: 'Golongan' },
|
||||||
|
{ label: 'Metode Pemberian' },
|
||||||
|
{ label: "Satuan" },
|
||||||
|
{ label: 'Stok' },
|
||||||
|
{ label: 'Aksi' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'group', 'method', 'unit', 'stock', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
group: (rec: unknown): unknown => {
|
||||||
|
return (rec as SmallDetailDto).medicineGroup?.name || '-'
|
||||||
|
},
|
||||||
|
method: (rec: unknown): unknown => {
|
||||||
|
return (rec as SmallDetailDto).medicineMethod?.name || '-'
|
||||||
|
},
|
||||||
|
unit: (rec: unknown): unknown => {
|
||||||
|
return (rec as SmallDetailDto).uom?.name || '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action: (rec: unknown, idx: number): RecComponent => {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
return props.rec.status_code === 1 ? 'default' : 'destructive'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant">
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-2 border-b border-b-slate-300 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label>No. IHS</Label>
|
||||||
|
<Field>
|
||||||
|
<Input />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import type {
|
||||||
|
Col,
|
||||||
|
KeyLabel,
|
||||||
|
RecComponent,
|
||||||
|
RecStrFuncComponent,
|
||||||
|
RecStrFuncUnknown,
|
||||||
|
Th,
|
||||||
|
} from '~/components/pub/my-ui/data/types'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
type SmallDetailDto = any
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
|
const _doctorStatus = {
|
||||||
|
0: 'Tidak Aktif',
|
||||||
|
1: 'Aktif',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cols: Col[] = [
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 250 },
|
||||||
|
{},
|
||||||
|
{ width: 100 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{ width: 120 },
|
||||||
|
{ width: 100 },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const header: Th[][] = [
|
||||||
|
[
|
||||||
|
{ label: 'Kode JKN' },
|
||||||
|
{ label: 'Nama' },
|
||||||
|
{ label: 'No KTP' },
|
||||||
|
{ label: 'No SIP' },
|
||||||
|
{ label: 'No IHS' },
|
||||||
|
{ label: 'Telpon' },
|
||||||
|
{ label: 'Fee Ranap' },
|
||||||
|
{ label: 'Fee Rajal' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
export const keys = [
|
||||||
|
'bpjs_code',
|
||||||
|
'name',
|
||||||
|
'identity_number',
|
||||||
|
'sip_no',
|
||||||
|
'ihs_number',
|
||||||
|
'phone',
|
||||||
|
'inPatient_itemPrice',
|
||||||
|
'outPatient_itemPrice',
|
||||||
|
'status',
|
||||||
|
'action',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: RecStrFuncUnknown = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.frontTitle} ${recX.name} ${recX.endTitle}`.trim()
|
||||||
|
},
|
||||||
|
identity_number: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
|
||||||
|
return '(TANPA NIK)'
|
||||||
|
}
|
||||||
|
return recX.identity_number
|
||||||
|
},
|
||||||
|
inPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.inPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
outPatient_itemPrice: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return Number(recX.outPatient_itemPrice.price).toLocaleString('id-ID')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: RecStrFuncUnknown = {
|
||||||
|
patient_address(_rec) {
|
||||||
|
return '-'
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubMyUiDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
Loaded 100 of 892 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in New Issue
Block a user