Compare commits
1
Commits
bd48cc5907
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28e6383d14 |
@@ -1,30 +0,0 @@
|
||||
# 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
|
||||
@@ -1,16 +0,0 @@
|
||||
NUXT_MAIN_API_ORIGIN=
|
||||
NUXT_BPJS_API_ORIGIN=
|
||||
NUXT_API_VCLAIM_SWAGGER= # https://vclaim-api.multy.chat
|
||||
NUXT_SYNC_API_ORIGIN=
|
||||
NUXT_API_ORIGIN=
|
||||
|
||||
SSO_CONFIRM_URL =
|
||||
|
||||
X_AP_CODE=rssa-sso
|
||||
X_AP_SECRET_KEY=sapiperah
|
||||
KEYCLOAK_LOGOUT_REDIRECT=http://localhost:3000/
|
||||
|
||||
# test local
|
||||
KEYCLOAK_REALM=rssa_testing
|
||||
KEYCLOAK_URL=http://127.0.0.1:8080/
|
||||
CLIENT_ID=portal-simrs-new
|
||||
+8
-25
@@ -1,28 +1,11 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# editor
|
||||
.vscode
|
||||
*.swp
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120,
|
||||
"semi": false,
|
||||
"plugins": ["prettier-plugin-tailwindcss"],
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"singleAttributePerLine": true
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
# Build Stage
|
||||
FROM node:24-alpine AS build-stage
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Enable pnpm using corepack
|
||||
RUN corepack enable
|
||||
|
||||
# Copy pnpm related files and package.json to leverage Docker layer caching
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Install dependencies using pnpm
|
||||
# Using --frozen-lockfile ensures consistent installations based on pnpm-lock.yaml
|
||||
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store pnpm install --frozen-lockfile
|
||||
|
||||
# Copy the rest of the application files
|
||||
COPY . .
|
||||
|
||||
# Build the Vue.js application for production
|
||||
RUN pnpm build
|
||||
|
||||
# Production Stage
|
||||
FROM nginx:stable-alpine AS production-stage
|
||||
|
||||
# Copy the built Vue.js application from the build stage to Nginx's web root
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
|
||||
# Expose port 80 for Nginx
|
||||
EXPOSE 80
|
||||
|
||||
# Command to run Nginx in the foreground
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,75 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,112 +1,2 @@
|
||||
# SIMRS - FE
|
||||
# simrsx-fe
|
||||
|
||||
RSSA - Front End
|
||||
|
||||
> [!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
@@ -1,25 +0,0 @@
|
||||
<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>
|
||||
@@ -1,378 +0,0 @@
|
||||
/* 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: 40 70% 60%;
|
||||
--secondary-foreground: 210 20% 100%;
|
||||
--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 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;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' width='250' height='30' viewBox='0 0 1000 120'><rect fill='#000000' width='1000' height='120'/><g fill='none' stroke='#222' stroke-width='10' stroke-opacity='1'><path d='M-500 75c0 0 125-30 250-30S0 75 0 75s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 45c0 0 125-30 250-30S0 45 0 45s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 105c0 0 125-30 250-30S0 105 0 105s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 15c0 0 125-30 250-30S0 15 0 15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500-15c0 0 125-30 250-30S0-15 0-15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 135c0 0 125-30 250-30S0 135 0 135s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/></g></svg>
|
||||
@@ -1,468 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { type Duration, intervalToDuration } from 'date-fns'
|
||||
|
||||
// schema
|
||||
import { type ActionReportFormData, ActionReportSchema } from '~/schemas/action-report.schema'
|
||||
|
||||
// type
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
import { type ClickType as ActionClickType } from '~/components/pub/my-ui/nav-footer/index'
|
||||
|
||||
// components
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||
import { ArrayMessage } from '~/components/pub/ui/form'
|
||||
import ActionForm from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
|
||||
// form field components
|
||||
import {
|
||||
FillNotes,
|
||||
RadioBloods,
|
||||
SelectBilling,
|
||||
SelectBirthPlace,
|
||||
SelectBirthType,
|
||||
SelectOperationSystem,
|
||||
SelectOperationType,
|
||||
SelectSpecimen,
|
||||
SelectSurgeryCounter,
|
||||
SelectSurgeryType,
|
||||
} from './fields'
|
||||
import { ButtonAction, Fragment, InputBase, TextAreaInput } from '~/components/pub/my-ui/form/'
|
||||
import { SelectDoctor } from '~/components/app/doctor/fields'
|
||||
// Helpers
|
||||
|
||||
// #region Props & Emits
|
||||
interface FormData extends ActionReportFormData {
|
||||
_operationDuration: string
|
||||
_anesthesiaDuration: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
mode: 'add' | 'edit' | 'view'
|
||||
initialValues?: Partial<FormData>
|
||||
|
||||
// form related
|
||||
doctors: Doctor[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'submit', payload: FormData): void
|
||||
(e: 'back'): void
|
||||
(e: 'error', errors: Error): void
|
||||
}>()
|
||||
|
||||
const tissueNotesLimit = 5
|
||||
const mode = toRef(props, 'mode')
|
||||
const isLoading = toRef(props, 'isLoading')
|
||||
|
||||
const isReadonly = computed(() => {
|
||||
if (isLoading.value === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (mode.value === 'view') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const formSchema = toTypedSchema(ActionReportSchema)
|
||||
|
||||
const { errors, handleSubmit, values, meta, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'encounterActionReportForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues ? props.initialValues : {},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
const onFormActionClicked = (action: ActionClickType) => {
|
||||
if (action === 'back') {
|
||||
emit('back')
|
||||
return
|
||||
}
|
||||
if (action === 'submit') {
|
||||
onSubmit()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = handleSubmit(
|
||||
(values) => {
|
||||
console.log(JSON.stringify(values))
|
||||
emit('submit', values)
|
||||
},
|
||||
(errors) => {
|
||||
console.error(errors)
|
||||
emit('error', new Error('Silahkan lengkapi form terlebih dahulu'))
|
||||
},
|
||||
)
|
||||
// #endregion
|
||||
|
||||
// #region Watcher
|
||||
watch(
|
||||
() => [values.operationExecution.operationStartAt, values.operationExecution.operationEndAt],
|
||||
([start, end]) => {
|
||||
if (!start || !end) return
|
||||
const pStart = new Date(start)
|
||||
const pEnd = new Date(end)
|
||||
|
||||
const formatTime = (r: Duration) =>
|
||||
[r.hours && `${r.hours} jam`, r.minutes && `${r.minutes} menit`, r.seconds && `${r.seconds} detik`]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const res = intervalToDuration({
|
||||
start: pStart,
|
||||
end: pEnd,
|
||||
})
|
||||
|
||||
setFieldValue('_operationDuration', formatTime(res))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => [values.operationExecution.anesthesiaStartAt, values.operationExecution.anesthesiaEndAt],
|
||||
([start, end]) => {
|
||||
if (!start || !end) return
|
||||
const pStart = new Date(start)
|
||||
const pEnd = new Date(end)
|
||||
|
||||
const formatTime = (r: Duration) =>
|
||||
[r.hours && `${r.hours} jam`, r.minutes && `${r.minutes} menit`, r.seconds && `${r.seconds} detik`]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const res = intervalToDuration({
|
||||
start: pStart,
|
||||
end: pEnd,
|
||||
})
|
||||
|
||||
setFieldValue('_anesthesiaDuration', formatTime(res))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="onSubmit">
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Tim Pelaksana Tindakan"
|
||||
>
|
||||
<p class="text-lg font-semibold">{{ section }}</p>
|
||||
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectDoctor
|
||||
fieldName="operatorTeam.dpjpId"
|
||||
label="Dokter Pemeriksa"
|
||||
placeholder="Pilih dokter"
|
||||
:doctors="doctors"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.operatorName"
|
||||
label="Operator"
|
||||
placeholder="Masukkan operator"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.assistantOperatorName"
|
||||
label="Asisten Operator"
|
||||
placeholder="Masukkan asisten operator"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.instrumentNurseName"
|
||||
label="Instrumentir"
|
||||
placeholder="Masukkan instrumentir"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.surgeryDate"
|
||||
label="Tanggal Pembedahan"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
placeholder=""
|
||||
/>
|
||||
</DE.Block>
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<TextAreaInput
|
||||
field-name="operatorTeam.actionDiagnosis"
|
||||
label="Diagnosa Tindakan"
|
||||
placeholder="Masukkan diagnosa tindakan"
|
||||
:col-span="2"
|
||||
:rows="5"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.postSurgeryNurseId"
|
||||
label="Perawat Pasca Bedah"
|
||||
placeholder="Masukkan perawat pasca bedah"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
</Fragment>
|
||||
|
||||
<Separator class="my-4" />
|
||||
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Tindakan Operatif/Non Operatif Lain"
|
||||
>
|
||||
<!-- <p class="text-lg font-semibold">{{ section }}</p> -->
|
||||
<DE.Block
|
||||
:col-count="2"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<DE.Cell>
|
||||
<slot name="procedures" />
|
||||
<ArrayMessage
|
||||
class="mt-1"
|
||||
v-if="meta.touched"
|
||||
name="procedures"
|
||||
/>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</Fragment>
|
||||
|
||||
<Separator class="my-4" />
|
||||
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Data Pelaksanaan Operasi"
|
||||
>
|
||||
<p class="text-lg font-semibold">{{ section }}</p>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectSurgeryType
|
||||
field-name="operationExecution.surgeryType"
|
||||
label="Jenis Operasi"
|
||||
placeholder="Pilih jenis operasi"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBilling
|
||||
field-name="operationExecution.billingCode"
|
||||
label="Kode Billing"
|
||||
placeholder="Pilih kode billing"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectOperationSystem
|
||||
field-name="operationExecution.operationSystem"
|
||||
label="Sistem Operasi"
|
||||
placeholder="Pilih sistem operasi"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="operationExecution.operationStartAt"
|
||||
label="Operasi Mulai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.operationEndAt"
|
||||
label="Operasi Selesai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="_operationDuration"
|
||||
label="Lama Operasi"
|
||||
placeholder="-"
|
||||
is-disabled
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="operationExecution.anesthesiaStartAt"
|
||||
label="Pembiusan Mulai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.anesthesiaEndAt"
|
||||
label="Pembiusan Selesai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="_anesthesiaDuration"
|
||||
label="Lama Pembiusan"
|
||||
placeholder="-"
|
||||
is-disabled
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectOperationType
|
||||
field-name="operationExecution.surgeryCleanType"
|
||||
label="Jenis Pembedahan"
|
||||
placeholder="Pilih jenis pembedahan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectSurgeryCounter
|
||||
field-name="operationExecution.surgeryNumber"
|
||||
label="Operasi Ke"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBirthType
|
||||
field-name="operationExecution.birthRemark"
|
||||
label="Keterangan Lahir"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBirthPlace
|
||||
field-name="operationExecution.birthPlaceNote"
|
||||
label="Ket. Tempat Lahir"
|
||||
placeholder="Pilih keterangan tempat lahir"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="operationExecution.personWeight"
|
||||
label="Berat Badan"
|
||||
placeholder="Masukkan berat badan"
|
||||
numeric-only
|
||||
suffix-msg="Gram"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.birthCondition"
|
||||
label="Ket. Saat Lahir"
|
||||
placeholder="Tambah catatan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<TextAreaInput
|
||||
field-name="operationExecution.operationDescription"
|
||||
label="Uraian Operasi"
|
||||
placeholder="Masukkan uraian"
|
||||
:rows="3"
|
||||
:col-span="2"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.bleedingAmountCc"
|
||||
label="Jumlah Pendarahan"
|
||||
placeholder="Masukkan jumlah pendarahan"
|
||||
suffix-msg="CC"
|
||||
numeric-only
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<RadioBloods
|
||||
field-name="bloodInput"
|
||||
label="Jenis & Jumlah Darah Masuk"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="implant.brand"
|
||||
label="Merk"
|
||||
placeholder="Masukkan merk"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.name"
|
||||
label="Nama Implant"
|
||||
placeholder="Masukkan nama implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.sticker"
|
||||
label="Sticker/Nomer Register Implant"
|
||||
placeholder="Masukkan sticker/nomor register implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.companionName"
|
||||
label="Nama Pendamping Implant"
|
||||
placeholder="Masukkan nama pendamping implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectSpecimen
|
||||
field-name="specimen.destination"
|
||||
label="Specimen/Jaringan dikirim ke"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
<FillNotes
|
||||
title="Keterangan Jaringan"
|
||||
:limit="tissueNotesLimit"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</Fragment>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ActionForm @click="onFormActionClicked" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@@ -1,65 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// components
|
||||
import { FieldArray } from 'vee-validate'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
// form field components
|
||||
import { ButtonAction, InputBase } from '~/components/pub/my-ui/form'
|
||||
|
||||
interface Props {
|
||||
isDisabled: boolean
|
||||
limit: number
|
||||
title: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const isReadonly = computed(() => props.isDisabled)
|
||||
</script>
|
||||
<template>
|
||||
<FieldArray
|
||||
v-slot="{ fields, push, remove }"
|
||||
name="tissueNotes"
|
||||
>
|
||||
<template v-if="fields.length === 0">
|
||||
{{ push({ note: '' }) }}
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<DE.Block
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
:label="idx === 0 ? 'Keterangan Jaringan' : undefined"
|
||||
:field-name="`tissueNotes[${idx}].note`"
|
||||
placeholder="Masukkan catatan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<DE.Cell class="flex items-start justify-start">
|
||||
<DE.Field :class="idx === 0 ? 'mt-[30px]' : 'mt-0'">
|
||||
<ButtonAction
|
||||
v-if="idx !== 0"
|
||||
:disabled="isReadonly"
|
||||
preset="delete"
|
||||
:title="`Hapus Kontak ${idx + 1}`"
|
||||
icon-only
|
||||
@click="remove(idx)"
|
||||
/>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
label="Tambah Catatan"
|
||||
title="Tambah Catatan Keterangan Jaringan"
|
||||
:disabled="fields.length >= limit || isReadonly"
|
||||
:full-width-mobile="true"
|
||||
class="mt-4"
|
||||
@click="push({ name: '', dose: '', unit: '' })"
|
||||
/>
|
||||
</FieldArray>
|
||||
</template>
|
||||
@@ -1,10 +0,0 @@
|
||||
export { default as FillNotes } from './fill-notes.vue'
|
||||
export { default as RadioBloods } from './radio-bloods.vue'
|
||||
export { default as SelectBilling } from './select-billing.vue'
|
||||
export { default as SelectBirthPlace } from './select-birth-place.vue'
|
||||
export { default as SelectBirthType } from './select-birth-type.vue'
|
||||
export { default as SelectOperationSystem } from './select-operation-system.vue'
|
||||
export { default as SelectOperationType } from './select-operation-type.vue'
|
||||
export { default as SelectSpecimen } from './select-specimen.vue'
|
||||
export { default as SelectSurgeryCounter } from './select-surgery-counter.vue'
|
||||
export { default as SelectSurgeryType } from './select-surgery-type.vue'
|
||||
@@ -1,101 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
|
||||
import { Label as RadioLabel } from '~/components/pub/ui/label'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
class?: string
|
||||
radioGroupClass?: string
|
||||
radioItemClass?: string
|
||||
labelClass?: string
|
||||
isDisabled?: boolean
|
||||
}>()
|
||||
|
||||
const { class: containerClass, radioGroupClass, radioItemClass, labelClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'PRC', value: 'prc' },
|
||||
{ label: 'WB', value: 'wb' },
|
||||
{ label: 'FFP', value: 'ffp' },
|
||||
{ label: 'TC', value: 'tc' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell
|
||||
:class="cn('radio-group-field', containerClass)"
|
||||
:col-span="2"
|
||||
>
|
||||
<DE.Label :label-for="fieldName">
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field :id="fieldName">
|
||||
<FormField
|
||||
v-slot="{ componentField: radioField }"
|
||||
:name="`${fieldName}.type`"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
:model-value="radioField.modelValue"
|
||||
@update:model-value="radioField.onChange"
|
||||
:class="cn('grid grid-cols-1 items-start gap-4 sm:grid-cols-2 ', radioGroupClass)"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in opts"
|
||||
:key="option.value"
|
||||
:class="cn('grid grid-cols-1 items-start gap-3 sm:grid-cols-[auto,1fr]', radioItemClass)"
|
||||
>
|
||||
<div class="flex min-w-fit items-center gap-2">
|
||||
<RadioGroupItem
|
||||
:id="`type-${index}`"
|
||||
:value="option.value"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
<RadioLabel :for="`type-${index}`">
|
||||
{{ option.label }}
|
||||
</RadioLabel>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
v-slot="{ componentField: amountField }"
|
||||
:name="`${fieldName}.amount.${option.value}`"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div class="relative w-[140px]">
|
||||
<Input
|
||||
v-bind="amountField"
|
||||
placeholder="00"
|
||||
class="pr-10"
|
||||
:disabled="radioField.modelValue !== option.value || isDisabled"
|
||||
@input="
|
||||
(e: InputEvent) => {
|
||||
const target = e.target as HTMLInputElement
|
||||
const v = target.value.replace(/\D/g, '')
|
||||
amountField.onChange(v)
|
||||
}
|
||||
"
|
||||
/>
|
||||
<span
|
||||
class="absolute inset-y-0 end-0 flex items-center justify-center px-2 text-sm text-muted-foreground"
|
||||
>
|
||||
CC
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,68 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'General', value: 'general' },
|
||||
{ label: 'Regional', value: 'regional' },
|
||||
{ label: 'Local', value: 'local' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,71 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'RSSA', value: 'rssa' },
|
||||
{ label: 'Bidan Luar', value: 'out1' },
|
||||
{ label: 'Dokter Luar', value: 'out2' },
|
||||
{ label: 'Dukun Bayi', value: 'out3' },
|
||||
{ label: 'Puskesmas', value: 'out4' },
|
||||
{ label: 'Paramedis Luar', value: 'out5' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,67 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Lahir Hidup', value: 'lahir_hidup' },
|
||||
{ label: 'Lahir Mati', value: 'lahir_mati' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Cito', value: 'cito' },
|
||||
{ label: 'Urgent', value: 'urgent' },
|
||||
{ label: 'Efektif', value: 'efektif' },
|
||||
{ label: 'Khusus', value: 'khusus' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Bersih', value: 'bersih' },
|
||||
{ label: 'Bersih Terkontaminasi', value: 'bersih_terkontaminasi' },
|
||||
{ label: 'Terkontaminasi Kotor', value: 'terkontaminasi' },
|
||||
{ label: 'Kotor', value: 'kotor' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'PA', value: 'pa' },
|
||||
{ label: 'Mikrobiologi', value: 'microbiology' },
|
||||
{ label: 'Laborat', value: 'laboratory' },
|
||||
{ label: 'Tidak Perlu', value: 'none' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,67 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: '1 (Satu)', value: 'first' },
|
||||
{ label: 'Ulangan', value: 'retry' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Kecil', value: 'kecil' },
|
||||
{ label: 'Sedang', value: 'sedang' },
|
||||
{ label: 'Besar', value: 'besar' },
|
||||
{ label: 'Khusus', value: 'khusus' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,88 +0,0 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-d.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL LAPORAN' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'OPERATOR' },
|
||||
{ label: 'TANGGAL PEMBEDAHAN' },
|
||||
{ label: 'JENIS OPERASI' },
|
||||
{ label: 'KODE BILLING' },
|
||||
{ label: 'SISTEM OPERASI' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'billing', 'system', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).operationAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return 'dr. Dewi Arum Sawitri, Sp.An'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return 'Besar'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return 'dr. Irwansyah Kurniawan Sp.Bo'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,89 +0,0 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL LAPORAN' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'OPERATOR' },
|
||||
{ label: 'TANGGAL PEMBEDAHAN' },
|
||||
{ label: 'JENIS OPERASI' },
|
||||
{ label: 'KODE BILLING' },
|
||||
{ label: 'SISTEM OPERASI' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'billing', 'system', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).operationAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return 'dr. Dewi Arum Sawitri, Sp.An'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return 'Besar'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return 'dr. Irwansyah Kurniawan Sp.Bo'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,166 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
// type
|
||||
import { type ProcedureSrc } from '~/models/procedure-src'
|
||||
import { type ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
|
||||
// componenets
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/components/pub/ui/accordion'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
||||
import ArrangementProcedurePicker from '~/components/app/therapy-protocol/picker-dialog/arrangement-procedure/procedure-picker.vue'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
data: ActionReportFormData
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'back'): void
|
||||
(e: 'edit'): void
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const { operatorTeam, procedures, operationExecution, bloodInput, implant, specimen, tissueNotes = [] } = props.data
|
||||
|
||||
const procedureSampleData = procedures as unknown as ProcedureSrc[]
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
|
||||
// #endregion region
|
||||
// #region Utilities & event handlers
|
||||
function onNavigate(type: string) {
|
||||
if (type == 'back') emit('back')
|
||||
if (type == 'edit') emit('edit')
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DetailRow label="Tanggal Laporan">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
|
||||
<Accordion
|
||||
type="multiple"
|
||||
class="w-full"
|
||||
collapsible
|
||||
:default-value="['section-1', 'section-2', 'section-3']"
|
||||
>
|
||||
<AccordionItem value="section-1">
|
||||
<AccordionTrigger>Tim Pelaksanaan Tindakan</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<DetailRow label="DPJP">dr. Marcell Galliard Sp.Gr</DetailRow>
|
||||
<DetailRow label="Operator">Sumitro</DetailRow>
|
||||
<DetailRow label="Asisten Operator">Alexis Lewis Carol</DetailRow>
|
||||
<DetailRow label="Instrumentir">Mikel Arteta</DetailRow>
|
||||
<DetailRow label="Tanggal Pembedahan">
|
||||
{{ format(new Date(), 'd MMMM yyyy', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Diagnosa Tindakan">{{ operatorTeam?.actionDiagnosis || '-' }}</DetailRow>
|
||||
<DetailRow label="Perawat Pasca Bedah">Cak Armuji</DetailRow>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="section-2">
|
||||
<AccordionTrigger>Tindakan Operatif / Non Operatif Lain</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<ArrangementProcedurePicker
|
||||
field-name="procedures"
|
||||
title="List Prosedur"
|
||||
sub-title="Pilih Prosedur"
|
||||
:mode="'preview'"
|
||||
:sample-items="procedureSampleData"
|
||||
/>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="section-3">
|
||||
<AccordionTrigger>Data Pelaksanaan Tindakan</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<DetailRow label="Jenis Operasi">dr. Marcell Galliard Sp.Gr</DetailRow>
|
||||
<DetailRow label="Kode Billing">GCASH1128190</DetailRow>
|
||||
<DetailRow label="Sistem Operasi">Alexis Lewis Carol</DetailRow>
|
||||
<DetailRow label="Operasi Mulai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Operasi Selesai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Lama Operasi">5 menit</DetailRow>
|
||||
<DetailRow label="Pembiusan Mulai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Pembiusan Selesai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Lama Pembiusan">5 menit</DetailRow>
|
||||
|
||||
<DetailRow label="PRC">300 CC</DetailRow>
|
||||
<DetailRow label="FPP">-</DetailRow>
|
||||
<DetailRow label="WB">-</DetailRow>
|
||||
<DetailRow label="TC">-</DetailRow>
|
||||
<DetailRow label="Merk">-</DetailRow>
|
||||
<DetailRow label="Nama Implant">-</DetailRow>
|
||||
<DetailRow label="Sticker / Nomor Register Implant">-</DetailRow>
|
||||
<DetailRow label="Nama Pendamping Implant">-</DetailRow>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DetailRow label="Jenis Pembedahan">Bersih</DetailRow>
|
||||
<DetailRow label="Operasi ke">1 (Satu)</DetailRow>
|
||||
<DetailRow label="Keterangan Lahir">Lahir Hidup</DetailRow>
|
||||
<DetailRow label="Ket. Tempat Lahir">RSSA</DetailRow>
|
||||
<DetailRow label="Berat Badan">18 gram</DetailRow>
|
||||
<DetailRow label="Ket. Saat Lahir">Normal dan sehat</DetailRow>
|
||||
<DetailRow label="Uraian Operasi">-</DetailRow>
|
||||
<DetailRow label="Jumlah Pendarahan">300 CC</DetailRow>
|
||||
<DetailRow label="Specimen / Jaringan dikirim ke">PA</DetailRow>
|
||||
<DetailRow label="Keterangan Jaringan">
|
||||
<ul
|
||||
class="list-disc space-y-1 pl-5 text-sm"
|
||||
v-if="tissueNotes.length > 0"
|
||||
v-for="item in tissueNotes"
|
||||
>
|
||||
<li>{{ item.note }}</li>
|
||||
</ul>
|
||||
<span v-else>-</span>
|
||||
</DetailRow>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<div class="my-2 flex justify-end py-2">
|
||||
<PubMyUiNavFooterBaEd @click="onNavigate" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -1,54 +0,0 @@
|
||||
import { addWeeks, formatISO } from 'date-fns'
|
||||
|
||||
export type ActionReportData = {
|
||||
id: number
|
||||
reportAt: string
|
||||
operationAt: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ActionReportData[] = [
|
||||
{
|
||||
id: 1,
|
||||
reportAt: formatISO(addWeeks(new Date(), -1)),
|
||||
operationAt: formatISO(addWeeks(new Date(), 1)),
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reportAt: new Date().toISOString(),
|
||||
operationAt: formatISO(addWeeks(new Date(), 2)),
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}],
|
||||
|
||||
headers: [[{ label: 'Kode' }, { label: 'Nama' }]],
|
||||
|
||||
keys: ['code', 'name'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
},
|
||||
|
||||
components: {
|
||||
},
|
||||
|
||||
htmls: {
|
||||
},
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// Pub components
|
||||
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
defineProps<{
|
||||
data: any[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<DataTable v-bind="config" :rows="data" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,58 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, { width: 50 }],
|
||||
|
||||
headers: [[
|
||||
{ label: 'Tgl. Order' },
|
||||
{ label: 'No. Order' },
|
||||
{ label: 'Jadwal Pemeriksaan' },
|
||||
{ label: 'Lokalisasi' },
|
||||
{ label: 'Stadium' },
|
||||
{ label: 'Status' },
|
||||
{ label: 'Resume' },
|
||||
{ label: '' }]],
|
||||
|
||||
keys: [
|
||||
'date',
|
||||
'number',
|
||||
'examinationDate',
|
||||
'localization',
|
||||
'stadium',
|
||||
'resume',
|
||||
'',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'date', label: 'Tanggal' },
|
||||
{ key: 'number', label: 'Nomor' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
props: {
|
||||
size: 'sm',
|
||||
},
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||
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 { config } 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">
|
||||
<DataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,47 +0,0 @@
|
||||
<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>
|
||||
@@ -1,114 +0,0 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{ width: 100 },
|
||||
{ width: 120 },
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{ width: 100 },
|
||||
{ width: 100 },
|
||||
{},
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Nama' },
|
||||
{ label: 'Rekam Medis' },
|
||||
{ label: 'KTP' },
|
||||
{ label: 'Tgl Lahir' },
|
||||
{ label: 'Umur' },
|
||||
{ label: 'JK' },
|
||||
{ label: 'Pendidikan' },
|
||||
{ label: 'Status' },
|
||||
{ label: '' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'name',
|
||||
'medicalRecord_number',
|
||||
'identity_number',
|
||||
'birth_date',
|
||||
'patient_age',
|
||||
'gender',
|
||||
'education',
|
||||
'status',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
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 '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec: unknown, idx: number) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
patient_address(rec: unknown) {
|
||||
return '-'
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { config } from './list-cfg'
|
||||
|
||||
defineProps<{
|
||||
data: any[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PubMyUiDataTable v-bind="config" :rows="data" />
|
||||
</template>
|
||||
@@ -1,215 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import { FieldArray, useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
||||
// types
|
||||
// import { type AssessmentEducationFormData, AssessmentEducationSchema, encode } from '~/schemas/assessment-education'
|
||||
|
||||
// componenets
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||
import { BaseSelect, CheckboxGeneral, CheckboxSpecial } from './fields'
|
||||
import { ButtonAction, TextAreaInput } from '~/components/pub/my-ui/form'
|
||||
import ActionForm from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
|
||||
// constant
|
||||
import {
|
||||
abilityCode,
|
||||
willCode,
|
||||
medObstacleCode,
|
||||
learnMethodCode,
|
||||
langClassCode,
|
||||
translatorSrcCode,
|
||||
} from '~/lib/clinical.constants'
|
||||
|
||||
interface FormData {}
|
||||
interface Props {
|
||||
noteLimit?: number
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
mode: 'add' | 'edit' | 'view'
|
||||
initialValues?: Partial<FormData>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isLoading: false,
|
||||
isReadonly: false,
|
||||
noteLimit: 10,
|
||||
})
|
||||
const isDisabled = computed(() => props.isLoading || props.isReadonly)
|
||||
// const formSchema = toTypedSchema(AssessmentEducationSchema)
|
||||
const formSchema = toTypedSchema(z.object({}))
|
||||
const { errors, handleSubmit, values, meta, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'assessmentEducationForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues
|
||||
? props.initialValues
|
||||
: {
|
||||
plans: [
|
||||
{
|
||||
id: 1,
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="">
|
||||
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Kebutuhan Edukasi</p>
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<CheckboxGeneral
|
||||
field-name="generalEducationNeeds"
|
||||
label="Informasi Umum"
|
||||
:col-span="2"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
<CheckboxSpecial
|
||||
field-name="specificEducationNeeds"
|
||||
label="Edukasi Khusus"
|
||||
:col-span="2"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<div class="h-6">
|
||||
<Separator />
|
||||
</div>
|
||||
|
||||
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Asesmen Kemampuan dan Kemauan Belajar</p>
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<BaseSelect
|
||||
field-name="learningAbility"
|
||||
label="Kemampuan Belajar"
|
||||
:items="abilityCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="learningWillingness"
|
||||
label="Kemauan Belajar"
|
||||
:items="willCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="barrier"
|
||||
label="Hambatan"
|
||||
:items="medObstacleCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="learningMethod"
|
||||
label="Metode Pembelajaran"
|
||||
:items="learnMethodCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="language"
|
||||
label="Bahasa"
|
||||
:items="langClassCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="languageBarrier"
|
||||
label="Hambatan Bahasa"
|
||||
:items="translatorSrcCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="beliefValue"
|
||||
label="Keyakinan pada Nilai-Nilai yang Dianut"
|
||||
:items="{
|
||||
ya: 'IYA',
|
||||
tidak: 'TIDAK',
|
||||
}"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
</DE.Block>
|
||||
<div class="h-6">
|
||||
<Separator />
|
||||
</div>
|
||||
|
||||
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Rencana Edukasi</p>
|
||||
<DE.Block
|
||||
:col-count="1"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<FieldArray
|
||||
v-slot="{ fields, push, remove }"
|
||||
name="tissueNotes"
|
||||
>
|
||||
<template v-if="fields.length === 0">
|
||||
{{ push({ note: '' }) }}
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<DE.Block
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
:col-count="6"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<DE.Cell :col-span="5">
|
||||
<TextAreaInput
|
||||
:field-name="`plans[${idx}].value`"
|
||||
:label="'Rencana ' + Number(idx + 1)"
|
||||
placeholder="Masukkan rencana catatan"
|
||||
:col-span="2"
|
||||
:rows="5"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Cell>
|
||||
<DE.Cell class="flex items-start justify-start">
|
||||
<DE.Field :class="idx === 0 ? 'mt-[30px]' : 'mt-0'">
|
||||
<ButtonAction
|
||||
v-if="idx !== 0"
|
||||
:disabled="isReadonly"
|
||||
preset="delete"
|
||||
:title="`Hapus Rencana ${idx + 1}`"
|
||||
icon-only
|
||||
@click="remove(idx)"
|
||||
/>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
|
||||
<div class="self-center pt-3">
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
label="Tambah Rencana"
|
||||
title="Tambah Rencana Edukasi"
|
||||
:disabled="fields.length >= noteLimit || isReadonly"
|
||||
:full-width-mobile="true"
|
||||
class="mt-4"
|
||||
@click="push({ id: fields.length + 1, value: '' })"
|
||||
/>
|
||||
</div>
|
||||
</FieldArray>
|
||||
</DE.Block>
|
||||
<!-- todo -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ActionForm @click="() => {}" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@@ -1,66 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { Checkbox } from '~/components/pub/ui/checkbox'
|
||||
|
||||
import type { CheckItem } from '~/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
label: string
|
||||
fieldName: string
|
||||
items: CheckItem[]
|
||||
isDisabled?: boolean
|
||||
colSpan?: number
|
||||
}>()
|
||||
|
||||
const { label, fieldName, items } = props
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :col-span="colSpan || 1">
|
||||
<DE.Label :label-for="fieldName">
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
|
||||
<DE.Field :id="fieldName">
|
||||
<FormField
|
||||
:name="fieldName"
|
||||
v-slot="{ value, handleChange }"
|
||||
>
|
||||
<FormItem>
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="ml-1 flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
:disabled="isDisabled"
|
||||
:id="`cb-${fieldName}-${item.id}`"
|
||||
:checked="value?.includes(item.id)"
|
||||
@update:checked="
|
||||
(checked) => {
|
||||
const newValue = [...(value || [])]
|
||||
if (checked) {
|
||||
if (!newValue.includes(item.id)) newValue.push(item.id)
|
||||
} else {
|
||||
const idx = newValue.indexOf(item.id)
|
||||
if (idx > -1) newValue.splice(idx, 1)
|
||||
}
|
||||
handleChange(newValue)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel
|
||||
:for="`cb-${fieldName}-${item.id}`"
|
||||
class="font-normal leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 md:!text-xs 2xl:text-sm"
|
||||
>
|
||||
{{ item.label }}
|
||||
</FormLabel>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,79 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
items: Record<string, string>
|
||||
placeholder?: string
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const { placeholder = 'Pilih', class: containerClass, selectClass, fieldGroupClass, labelClass } = props
|
||||
|
||||
const opts = computed(() => {
|
||||
const data = mapToComboboxOptList(props.items)
|
||||
// hide code on select options
|
||||
const filtered = data
|
||||
.filter((item) => item.code)
|
||||
.reduce(
|
||||
(acc, item) => {
|
||||
acc.push({
|
||||
label: item.label as string,
|
||||
value: item.value as string,
|
||||
})
|
||||
return acc
|
||||
},
|
||||
[] as Array<{ label: string; value: string }>,
|
||||
)
|
||||
|
||||
return filtered
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,25 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import BaseCheckbox from './base-checkbox.vue'
|
||||
import { generalEduCode } from '~/lib/clinical.constants'
|
||||
import { mapToCheckItems } from '~/lib/utils'
|
||||
|
||||
interface Props {
|
||||
label: string
|
||||
fieldName: string
|
||||
isDisabled?: boolean
|
||||
colSpan?: number
|
||||
}
|
||||
defineProps<Props>()
|
||||
|
||||
const generalItems = mapToCheckItems(generalEduCode)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCheckbox
|
||||
:field-name="fieldName"
|
||||
:label="label"
|
||||
:is-disabled="isDisabled"
|
||||
:col-span="colSpan"
|
||||
:items="generalItems"
|
||||
/>
|
||||
</template>
|
||||
@@ -1,22 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import BaseCheckbox from './base-checkbox.vue'
|
||||
import { specialEduCode } from '~/lib/clinical.constants'
|
||||
import { mapToCheckItems } from '~/lib/utils'
|
||||
interface Props {
|
||||
label: string
|
||||
fieldName: string
|
||||
colSpan?: number
|
||||
}
|
||||
defineProps<Props>()
|
||||
|
||||
const specialItems = mapToCheckItems(specialEduCode)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCheckbox
|
||||
:field-name="fieldName"
|
||||
:label="label"
|
||||
:col-span="colSpan"
|
||||
:items="specialItems"
|
||||
/>
|
||||
</template>
|
||||
@@ -1,4 +0,0 @@
|
||||
export { default as BaseSelect } from './base-select.vue'
|
||||
export { default as CheckboxGeneral } from './checkbox-general.vue'
|
||||
export { default as CheckboxSpecial } from './checkbox-special.vue'
|
||||
export { default as SelectAssessmentCode } from './select-assessment-code.vue'
|
||||
@@ -1,36 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
label: string
|
||||
fieldName: string
|
||||
placeholder?: string
|
||||
colSpan?: number
|
||||
}>()
|
||||
|
||||
const { label, fieldName, placeholder = 'Masukkan catatan' } = props
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :col-span="colSpan || 1">
|
||||
<DE.Label :label-for="fieldName">
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field :id="fieldName">
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
v-bind="componentField"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,75 +0,0 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,75 +0,0 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,54 +0,0 @@
|
||||
import { addWeeks, formatISO } from 'date-fns'
|
||||
|
||||
export type ActionReportData = {
|
||||
id: number
|
||||
reportAt: string
|
||||
operationAt: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ActionReportData[] = [
|
||||
{
|
||||
id: 1,
|
||||
reportAt: formatISO(addWeeks(new Date(), -1)),
|
||||
operationAt: formatISO(addWeeks(new Date(), 1)),
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reportAt: new Date().toISOString(),
|
||||
operationAt: formatISO(addWeeks(new Date(), 2)),
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
@@ -1,99 +0,0 @@
|
||||
<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'
|
||||
import { useKeycloak } from "~/composables/useKeycloack"
|
||||
|
||||
interface Props {
|
||||
schema: z.ZodSchema<any>
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [data: any]
|
||||
sso: []
|
||||
}>()
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
const { initKeycloak, getProfile, loginSSO } = useKeycloak()
|
||||
const profile = ref<any>(null)
|
||||
|
||||
onMounted(async () => {
|
||||
await initKeycloak('check-sso')
|
||||
profile.value = getProfile()
|
||||
console.log(profile)
|
||||
})
|
||||
|
||||
const onSSO = (async () => {
|
||||
try {
|
||||
const redirect = window.location.origin + '/auth/sso'
|
||||
await loginSSO({ redirectUri: redirect })
|
||||
} catch (error) {
|
||||
console.error('Call SSO 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>
|
||||
|
||||
<Button @click="onSSO" target="_blank">
|
||||
Login SSO
|
||||
</Button>
|
||||
</template>
|
||||
@@ -1,150 +0,0 @@
|
||||
<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/combobox/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.bed,
|
||||
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 = props.values.parent_id ? Number(props.values.parent_id) : null
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
code.value = ''
|
||||
name.value = ''
|
||||
infraGroup_code.value = infraGroupCodesKeys.bed
|
||||
parent_id.value = null
|
||||
}
|
||||
|
||||
function onSubmitForm() {
|
||||
const formData: InfraFormData = {
|
||||
code: code.value || '',
|
||||
name: name.value || '',
|
||||
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.bed,
|
||||
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">Kamar</Label>
|
||||
<Field :errMessage="errors.parent_id">
|
||||
<Combobox
|
||||
id="parent"
|
||||
v-model="parent_id"
|
||||
v-bind="parentIdAttrs"
|
||||
:items="parents"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Kamar"
|
||||
search-placeholder="Cari Kamar"
|
||||
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>
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, { width: 50 }],
|
||||
|
||||
headers: [[
|
||||
{ label: 'Kode' },
|
||||
{ label: 'Nama' },
|
||||
{ label: 'Kamar' },
|
||||
{ label: '' },
|
||||
]],
|
||||
|
||||
keys: ['code', 'name', 'parent', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,90 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type LinkItem, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
rec: ListItemDto
|
||||
}>()
|
||||
|
||||
const recId = inject<Ref<number>>('rec_id')!
|
||||
const recAction = inject<Ref<string>>('rec_action')!
|
||||
const recItem = inject<Ref<any>>('rec_item')!
|
||||
const activeKey = ref<string | null>(null)
|
||||
const linkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Print',
|
||||
onClick: () => {
|
||||
print()
|
||||
},
|
||||
icon: 'i-lucide-printer',
|
||||
},
|
||||
{
|
||||
label: 'Log History',
|
||||
onClick: () => {
|
||||
history()
|
||||
},
|
||||
icon: 'i-lucide-logs',
|
||||
},
|
||||
{
|
||||
label: 'Hapus',
|
||||
onClick: () => {
|
||||
del()
|
||||
},
|
||||
icon: 'i-lucide-trash',
|
||||
},
|
||||
]
|
||||
|
||||
function print() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showProcess
|
||||
recItem.value = props.rec
|
||||
}
|
||||
|
||||
function history() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showDetail
|
||||
recItem.value = props.rec
|
||||
}
|
||||
|
||||
function del() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showConfirmDelete
|
||||
recItem.value = props.rec
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-chevrons-up-down"
|
||||
class="ml-auto size-4"
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||
align="end"
|
||||
>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem
|
||||
v-for="item in linkItems"
|
||||
:key="item.label"
|
||||
class="hover:bg-gray-100 dark:hover:bg-slate-700"
|
||||
@click="item.onClick"
|
||||
@mouseenter="activeKey = item.label"
|
||||
@mouseleave="activeKey = null"
|
||||
>
|
||||
<Icon :name="item.icon ?? ''" />
|
||||
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,49 +0,0 @@
|
||||
<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'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import SelectOriginPolyclinic from '~/components/app/bpjs/control-letter/_common/select-origin-polyclinic.vue'
|
||||
import SelectDestinationPolyclinic from '~/components/app/bpjs/control-letter/_common/select-destination-polyclinic.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
const props = defineProps()
|
||||
|
||||
const items = reactive([
|
||||
{ id: 1, description: 'Shipped from warehouse', createdAt: new Date(Date.now() - 86400000 * 2) },
|
||||
{ id: 2, description: 'In transit to distribution center', createdAt: new Date(Date.now() - 86400000) },
|
||||
{ id: 3, description: 'Out for delivery (Current)', createdAt: new Date() },
|
||||
])
|
||||
const itemsCount = computed(() => items.length || 0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul :class="cn('pb-5 flex flex-col min-h-[30rem]', '')">
|
||||
<li v-for="(item, index) in items" :key="item.id" class="flex gap-3 items-start">
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="h-5 w-5 rounded-full border-2 border-gray-300 flex items-center justify-center">
|
||||
<div :class="cn('dark:bg-white border-gray-300 rounded-full p-1.5',
|
||||
index === 0 ? 'bg-green-500' : 'bg-transparent'
|
||||
)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr v-if="index !== itemsCount - 1" class="h-8 w-0.5 bg-gray-300 dark:bg-gray-300" aria-hidden="true">
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center min-w-96">
|
||||
<div class="max-w-80">
|
||||
<time :class="cn('font-medium text-gray-800 dark:text-gray-100', '')">
|
||||
{{ item?.createdAt.toLocaleDateString('id-ID') }}
|
||||
</time>
|
||||
<h1 :class="cn('text-gray-500 dark:text-gray-400', '')">{{ item.description }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -1,104 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { Calendar as CalendarIcon, Filter as FilterIcon, Search } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { DateRange } from 'radix-vue'
|
||||
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const dateRange = ref<{ from: Date | null; to: Date | null }>({
|
||||
from: new Date(),
|
||||
to: new Date(),
|
||||
})
|
||||
|
||||
const df = new DateFormatter('en-US', {
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
|
||||
const value = ref({
|
||||
start: new CalendarDate(2022, 1, 20),
|
||||
end: new CalendarDate(2022, 1, 20).add({ days: 20 }),
|
||||
}) as Ref<DateRange>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
:class="cn('w-full bg-white border-gray-400 justify-start text-left font-normal', !value && 'text-muted-foreground')"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<template v-if="value.start">
|
||||
<template v-if="value.end">
|
||||
{{ df.format(value.start.toDate(getLocalTimeZone())) }} -
|
||||
{{ df.format(value.end.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ df.format(value.start.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else> Pick a date </template>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<RangeCalendar
|
||||
v-model="value"
|
||||
initial-focus
|
||||
:number-of-months="2"
|
||||
@update:start-value="(startDate) => (value.start = startDate)"
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
// Generate job options from constants, sama seperti pola genderCodes
|
||||
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="jobOptions"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
// Generate job options from constants, sama seperti pola genderCodes
|
||||
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="jobOptions"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,128 +0,0 @@
|
||||
<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'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import SelectOriginPolyclinic from '~/components/app/bpjs/control-letter/_common/select-origin-polyclinic.vue'
|
||||
import SelectDestinationPolyclinic from '~/components/app/bpjs/control-letter/_common/select-destination-polyclinic.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import SelectDateRange from './_common/select-date-range.vue'
|
||||
|
||||
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]
|
||||
reset: [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 onResetForm({ resetForm }: { resetForm: () => void }) {
|
||||
emit('reset', 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-7 text-lg xl:text-xl">
|
||||
<div class="flex flex-col justify-between">
|
||||
<SelectDateRange
|
||||
field-name="releaseDate"
|
||||
label="Tanggal Penerbitan"
|
||||
placeholder="Tanggal Penerbitan"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectDateRange
|
||||
field-name="controlPlanDate"
|
||||
label="Tanggal Rencana Kontrol"
|
||||
placeholder="Tanggal Rencana Kontrol"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<InputBase
|
||||
field-name="patientName"
|
||||
label="Nama Pasien"
|
||||
placeholder="Nama Pasien"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="cardNumber"
|
||||
label="Nomor Kartu"
|
||||
placeholder="Nomor Kartu"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="sepNumber"
|
||||
label="Nomor SEP"
|
||||
placeholder="Nomor SEP"
|
||||
/>
|
||||
<SelectOriginPolyclinic
|
||||
field-name="originPolyclinic"
|
||||
label="Poliklinik Asal"
|
||||
placeholder="Pilih Poliklinik Asal"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectDestinationPolyclinic
|
||||
field-name="destinationPolyclinic"
|
||||
label="Poliklinik Tujuan"
|
||||
placeholder="Pilih Poliklinik Tujuan"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex items-center gap-3 justify-end">
|
||||
<Button @click="onResetForm" variant="secondary">Reset</Button>
|
||||
<Button @click="onSubmitForm">Terapkan</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -1,108 +0,0 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { educationCodes, genderCodes } from '~/lib/constants'
|
||||
import { calculateAge } from '~/lib/utils'
|
||||
|
||||
const action = defineAsyncComponent(() => import('./_common/dropdown-action.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('~/components/pub/my-ui/badge/status-badge.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {},{}, {}, {}, {}, {}, {width: 90},{width: 10},],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'No Surat' },
|
||||
{ label: 'No MR' },
|
||||
{ label: 'Nama' },
|
||||
{ label: 'Tgl Rencana Kontrol' },
|
||||
{ label: 'Tgl Penerbitan' },
|
||||
{ label: 'Klinik Asal' },
|
||||
{ label: 'Klinik Tujuan' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'No SEP Asal' },
|
||||
{ label: 'Status' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['birth_date', 'number', 'person.name', 'birth_date', 'birth_date',
|
||||
'birth_date', 'number', 'person.name', 'birth_date', 'status', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
patientId: (rec: unknown): unknown => {
|
||||
const patient = rec as Patient
|
||||
return patient.number
|
||||
},
|
||||
identity_number: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
|
||||
if (person.nationality == 'WNA') {
|
||||
return person.passportNumber
|
||||
}
|
||||
|
||||
return person.residentIdentityNumber || '-'
|
||||
},
|
||||
birth_date: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
|
||||
if (typeof person.birthDate == 'object' && person.birthDate) {
|
||||
return (person.birthDate as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof person.birthDate == 'string') {
|
||||
return (person.birthDate as string).substring(0, 10)
|
||||
}
|
||||
return person.birthDate
|
||||
},
|
||||
patient_age: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
return calculateAge(person.birthDate)
|
||||
},
|
||||
gender: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
|
||||
if (typeof person.gender_code == 'number' && person.gender_code >= 0) {
|
||||
return person.gender_code
|
||||
} else if (typeof person.gender_code === 'string' && person.gender_code) {
|
||||
return genderCodes[person.gender_code] || '-'
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
education: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
if (typeof person.education_code == 'number' && person.education_code >= 0) {
|
||||
return person.education_code
|
||||
} else if (typeof person.education_code === 'string' && person.education_code) {
|
||||
return educationCodes[person.education_code] || '-'
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
status(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
patient_address(_rec) {
|
||||
return '-'
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,108 +0,0 @@
|
||||
<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>
|
||||
@@ -1,36 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, { width: 50 }],
|
||||
|
||||
headers: [[
|
||||
{ label: 'Kode' },
|
||||
{ label: 'Nama' },
|
||||
{ label: '' },
|
||||
]],
|
||||
|
||||
keys: ['code', 'name', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,150 +0,0 @@
|
||||
<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/combobox/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.chamber,
|
||||
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 = props.values.parent_id ? Number(props.values.parent_id) : null
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
code.value = ''
|
||||
name.value = ''
|
||||
infraGroup_code.value = infraGroupCodesKeys.chamber
|
||||
parent_id.value = null
|
||||
}
|
||||
|
||||
function onSubmitForm() {
|
||||
const formData: InfraFormData = {
|
||||
code: code.value || '',
|
||||
name: name.value || '',
|
||||
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.chamber,
|
||||
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">Lantai</Label>
|
||||
<Field :errMessage="errors.parent_id">
|
||||
<Combobox
|
||||
id="parent"
|
||||
v-model="parent_id"
|
||||
v-bind="parentIdAttrs"
|
||||
:items="parents"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Lantai"
|
||||
search-placeholder="Cari Lantai"
|
||||
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>
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, { width: 50 }],
|
||||
|
||||
headers: [[
|
||||
{ label: 'Kode' },
|
||||
{ label: 'Nama' },
|
||||
{ label: 'Lantai' },
|
||||
{ label: '' },
|
||||
]],
|
||||
|
||||
keys: ['code', 'name', 'parent', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,30 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Button from '~/components/pub/ui/button/Button.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
rec: any
|
||||
idx?: number
|
||||
}>()
|
||||
|
||||
// Try to get proses handler from parent via inject
|
||||
const prosesHandler = inject<(rec: any) => void>('proses-handler', null)
|
||||
|
||||
function handleProses() {
|
||||
if (prosesHandler) {
|
||||
prosesHandler(props.rec)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
class="border-orange-500 bg-orange-500 text-white hover:bg-orange-600"
|
||||
@click="handleProses"
|
||||
>
|
||||
Proses
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '~/components/pub/ui/table'
|
||||
import Input from '~/components/pub/ui/input/Input.vue'
|
||||
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
|
||||
import Button from '~/components/pub/ui/button/Button.vue'
|
||||
import { format, parseISO } from 'date-fns'
|
||||
import { id as localeID } from 'date-fns/locale'
|
||||
|
||||
interface Props {
|
||||
open?: boolean
|
||||
tanggalPemeriksaan?: string | Date
|
||||
jadwalTanggalPemeriksaan?: string | Date
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
open: false,
|
||||
tanggalPemeriksaan: undefined,
|
||||
jadwalTanggalPemeriksaan: undefined,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:open': [value: boolean]
|
||||
'update:date': [value: string | undefined]
|
||||
'update:schedule': [value: string | undefined]
|
||||
submit: [data: { tanggalPemeriksaan: string | undefined; jadwalTanggalPemeriksaan: string | undefined }]
|
||||
}>()
|
||||
|
||||
// Local state for jadwal tanggal pemeriksaan
|
||||
const jadwalTanggal = ref<string | undefined>(
|
||||
props.jadwalTanggalPemeriksaan
|
||||
? typeof props.jadwalTanggalPemeriksaan === 'string'
|
||||
? props.jadwalTanggalPemeriksaan
|
||||
: format(props.jadwalTanggalPemeriksaan, 'yyyy-MM-dd')
|
||||
: undefined,
|
||||
)
|
||||
|
||||
// Watch for external changes
|
||||
watch(
|
||||
() => props.jadwalTanggalPemeriksaan,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
jadwalTanggal.value =
|
||||
typeof newValue === 'string' ? newValue : format(newValue, 'yyyy-MM-dd')
|
||||
} else {
|
||||
jadwalTanggal.value = undefined
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Watch local changes
|
||||
watch(jadwalTanggal, (newValue) => {
|
||||
emit('update:schedule', newValue)
|
||||
})
|
||||
|
||||
// Format date for display
|
||||
const formattedTanggalPemeriksaan = computed(() => {
|
||||
if (!props.tanggalPemeriksaan) return ''
|
||||
try {
|
||||
const date =
|
||||
props.tanggalPemeriksaan instanceof Date
|
||||
? props.tanggalPemeriksaan
|
||||
: parseISO(props.tanggalPemeriksaan)
|
||||
return format(date, 'dd MMMM yyyy', { locale: localeID })
|
||||
} catch {
|
||||
return props.tanggalPemeriksaan.toString()
|
||||
}
|
||||
})
|
||||
|
||||
// Handle submit
|
||||
function handleSubmit() {
|
||||
emit('submit', {
|
||||
tanggalPemeriksaan: props.tanggalPemeriksaan
|
||||
? typeof props.tanggalPemeriksaan === 'string'
|
||||
? props.tanggalPemeriksaan
|
||||
: format(props.tanggalPemeriksaan, 'yyyy-MM-dd')
|
||||
: undefined,
|
||||
jadwalTanggalPemeriksaan: jadwalTanggal.value,
|
||||
})
|
||||
}
|
||||
|
||||
// Table data for Jadwal Ruang Tindakan
|
||||
const scheduleData = [
|
||||
{
|
||||
no: 1,
|
||||
namaJenis: 'Ruang Tindakan',
|
||||
jenisPemeriksaan: 'KEMOTERAPI',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
:open="open"
|
||||
size="lg"
|
||||
title="Verifikasi Jadwal Pasien"
|
||||
@update:open="$emit('update:open', $event)"
|
||||
>
|
||||
<div class="space-y-6 py-4">
|
||||
<!-- Jadwal Ruang Tindakan Section -->
|
||||
<div class="space-y-3">
|
||||
<h4 class="text-base font-semibold">Jadwal Ruang Tindakan</h4>
|
||||
<div class="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader class="bg-gray-50">
|
||||
<TableRow>
|
||||
<TableHead class="font-semibold">NO</TableHead>
|
||||
<TableHead class="font-semibold">NAMA JENIS</TableHead>
|
||||
<TableHead class="font-semibold">JENIS PEMERIKSAAN</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-for="(row, index) in scheduleData" :key="index">
|
||||
<TableCell>{{ row.no }}</TableCell>
|
||||
<TableCell>{{ row.namaJenis }}</TableCell>
|
||||
<TableCell>{{ row.jenisPemeriksaan }}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tanggal Pemeriksaan Section -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">Tanggal Pemeriksaan</label>
|
||||
<Input
|
||||
:model-value="formattedTanggalPemeriksaan"
|
||||
:disabled="true"
|
||||
class="bg-gray-100 text-gray-700"
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Jadwal Tanggal Pemeriksaan Section -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">
|
||||
Jadwal Tanggal Pemeriksaan
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<DatepickerSingle
|
||||
v-model="jadwalTanggal"
|
||||
placeholder="Pilih Jadwal"
|
||||
:disabled="isLoading"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Action Button -->
|
||||
<div class="flex justify-end pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
:disabled="isLoading || !jadwalTanggal"
|
||||
class="bg-gradient-to-r from-orange-500 to-orange-400 hover:from-orange-600 hover:to-orange-500 text-white shadow-md"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<Icon name="i-lucide-calendar-check" class="mr-2 h-4 w-4" />
|
||||
Simpan Jadwal
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { LinkItem, ListItemDto } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
const props = defineProps<{
|
||||
rec: ListItemDto
|
||||
}>()
|
||||
|
||||
const recId = inject<Ref<number>>('rec_id')!
|
||||
const recAction = inject<Ref<string>>('rec_action')!
|
||||
const recItem = inject<Ref<any>>('rec_item')!
|
||||
const activeKey = ref<string | null>(null)
|
||||
const linkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Proses',
|
||||
onClick: () => {
|
||||
process()
|
||||
},
|
||||
icon: 'i-lucide-pencil',
|
||||
},
|
||||
]
|
||||
|
||||
function process() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = 'Process'
|
||||
recItem.value = props.rec
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-chevrons-up-down"
|
||||
class="ml-auto size-4"
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||
align="end"
|
||||
>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem
|
||||
v-for="item in linkItems"
|
||||
:key="item.label"
|
||||
class="hover:bg-gray-100 dark:hover:bg-slate-700"
|
||||
@click="item.onClick"
|
||||
@mouseenter="activeKey = item.label"
|
||||
@mouseleave="activeKey = null"
|
||||
>
|
||||
<Icon :name="item.icon ?? ''" />
|
||||
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,302 +0,0 @@
|
||||
<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 Input from '~/components/pub/ui/input/Input.vue'
|
||||
import Button from '~/components/pub/ui/button/Button.vue'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
|
||||
|
||||
// Helpers
|
||||
import type z from 'zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { chemotherapySchema } from "~/schemas/chemotherapy.schema"
|
||||
|
||||
interface Props {
|
||||
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 items = [
|
||||
{ value: 'item-1', label: 'Item 1' },
|
||||
{ value: 'item-2', label: 'Item 2' },
|
||||
{ value: 'item-3', label: 'Item 3' },
|
||||
]
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: any, resetForm: () => void]
|
||||
cancel: [resetForm: () => void]
|
||||
}>()
|
||||
|
||||
const { defineField, errors, meta } = useForm({
|
||||
validationSchema: toTypedSchema(chemotherapySchema),
|
||||
initialValues: {
|
||||
namaPasien: '',
|
||||
tanggalLahir: '',
|
||||
noRM: '',
|
||||
alamat: '',
|
||||
beratBadan: '',
|
||||
tinggiBadan: '',
|
||||
diagnosa: '',
|
||||
siklus: '',
|
||||
periodeAwal: '',
|
||||
periodeAkhir: '',
|
||||
tanggalKemoterapi: '',
|
||||
dokterKRJ: '',
|
||||
},
|
||||
})
|
||||
|
||||
// Define form fields
|
||||
const [namaPasien, namaPasienAttrs] = defineField('namaPasien')
|
||||
const [tanggalLahir, tanggalLahirAttrs] = defineField('tanggalLahir')
|
||||
const [noRM, noRMAttrs] = defineField('noRM')
|
||||
const [alamat, alamatAttrs] = defineField('alamat')
|
||||
const [beratBadan, beratBadanAttrs] = defineField('beratBadan')
|
||||
const [tinggiBadan, tinggiBadanAttrs] = defineField('tinggiBadan')
|
||||
const [diagnosa, diagnosaAttrs] = defineField('diagnosa')
|
||||
const [siklus, siklusAttrs] = defineField('siklus')
|
||||
const [periodeAwal, periodeAwalAttrs] = defineField('periodeAwal')
|
||||
const [periodeAkhir, periodeAkhirAttrs] = defineField('periodeAkhir')
|
||||
const [tanggalKemoterapi, tanggalKemoterapiAttrs] = defineField('tanggalKemoterapi')
|
||||
const [dokterKRJ, dokterKRJAttrs] = defineField('dokterKRJ')
|
||||
|
||||
// Set initial values if provided
|
||||
if (props.values) {
|
||||
// Object.entries(props.values).forEach(([key, value]) => {
|
||||
// if (value !== undefined) {
|
||||
// const field = defineField(key)[0]
|
||||
// field.value = value
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
// Object.keys(meta.value.initialValues).forEach((key) => {
|
||||
// const field = defineField(key)[0]
|
||||
// field.value = ''
|
||||
// })
|
||||
}
|
||||
|
||||
function onSubmitForm() {
|
||||
const formData = {
|
||||
namaPasien: namaPasien.value,
|
||||
tanggalLahir: tanggalLahir.value,
|
||||
noRM: noRM.value,
|
||||
alamat: alamat.value,
|
||||
beratBadan: beratBadan.value,
|
||||
tinggiBadan: tinggiBadan.value,
|
||||
diagnosa: diagnosa.value,
|
||||
siklus: siklus.value,
|
||||
periodeAwal: periodeAwal.value,
|
||||
periodeAkhir: periodeAkhir.value,
|
||||
tanggalKemoterapi: tanggalKemoterapi.value,
|
||||
dokterKRJ: dokterKRJ.value,
|
||||
}
|
||||
emit('submit', formData, resetForm)
|
||||
}
|
||||
|
||||
function onCancelForm() {
|
||||
emit('cancel', resetForm)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent>
|
||||
<!-- Data Pasien Section -->
|
||||
<div class="mb-6">
|
||||
<h3 class="mb-4 text-lg font-semibold">Data Pasien</h3>
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!mb-2.5 !pt-0 xl:!mb-3"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Nama Pasien</Label>
|
||||
<Field :errMessage="errors.namaPasien">
|
||||
<Input
|
||||
v-model="namaPasien"
|
||||
v-bind="namaPasienAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan nama pasien"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Tanggal Lahir</Label>
|
||||
<Field :errMessage="errors.tanggalLahir">
|
||||
<DatePicker
|
||||
v-model="tanggalLahir"
|
||||
v-bind="tanggalLahirAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih tanggal lahir"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">No. RM</Label>
|
||||
<Field :errMessage="errors.noRM">
|
||||
<Input
|
||||
v-model="noRM"
|
||||
v-bind="noRMAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan nomor RM"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Alamat</Label>
|
||||
<Field :errMessage="errors.alamat">
|
||||
<Input
|
||||
v-model="alamat"
|
||||
v-bind="alamatAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan alamat"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Berat Badan</Label>
|
||||
<Field :errMessage="errors.beratBadan">
|
||||
<Input
|
||||
v-model="beratBadan"
|
||||
v-bind="beratBadanAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan berat badan"
|
||||
type="number"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Tinggi Badan</Label>
|
||||
<Field :errMessage="errors.tinggiBadan">
|
||||
<Input
|
||||
v-model="tinggiBadan"
|
||||
v-bind="tinggiBadanAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan tinggi badan"
|
||||
type="number"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Diagnosa</Label>
|
||||
<Field :errMessage="errors.diagnosa">
|
||||
<Combobox
|
||||
id="diagnose"
|
||||
v-model="diagnosa"
|
||||
v-bind="diagnosaAttrs"
|
||||
:items="items"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Tentukan diagnosa pasien"
|
||||
search-placeholder="Cari diagnosa"
|
||||
empty-message="Diagnosa tidak ditemukan"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</div>
|
||||
|
||||
<!-- Protokol Kemoterapi Section -->
|
||||
<div class="mb-6">
|
||||
<h3 class="mb-4 text-lg font-semibold">Protokol Kemoterapi</h3>
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!mb-2.5 !pt-0 xl:!mb-3"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Siklus</Label>
|
||||
<Field :errMessage="errors.siklus">
|
||||
<Input
|
||||
v-model="siklus"
|
||||
v-bind="siklusAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan siklus"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Periode</Label>
|
||||
<div class="flex items-center gap-4">
|
||||
<Field
|
||||
:errMessage="errors.periodeAwal"
|
||||
class="flex-1"
|
||||
>
|
||||
<DatepickerSingle
|
||||
v-model="periodeAwal"
|
||||
v-bind="periodeAwalAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Mulai Periode"
|
||||
/>
|
||||
</Field>
|
||||
<span>Sampai</span>
|
||||
<Field
|
||||
:errMessage="errors.periodeAkhir"
|
||||
class="flex-1"
|
||||
>
|
||||
<DatepickerSingle
|
||||
v-model="periodeAkhir"
|
||||
v-bind="periodeAkhirAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Akhir Periode"
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Tanggal Kemoterapi</Label>
|
||||
<Field :errMessage="errors.tanggalKemoterapi">
|
||||
<DatepickerSingle
|
||||
v-model="tanggalKemoterapi"
|
||||
v-bind="tanggalKemoterapiAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih tanggal kemoterapi"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Label height="compact">Dokter Ruang Tindakan</Label>
|
||||
<Field :errMessage="errors.dokterKRJ">
|
||||
<Combobox
|
||||
id="doctor"
|
||||
v-model="dokterKRJ"
|
||||
v-bind="dokterKRJAttrs"
|
||||
:items="items"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih dokter"
|
||||
search-placeholder="Cari dokter"
|
||||
empty-message="Dokter tidak ditemukan"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</div>
|
||||
|
||||
<!-- Form Actions -->
|
||||
<div class="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>
|
||||
@@ -1,37 +0,0 @@
|
||||
<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 { config } from './list-cfg.admin'
|
||||
|
||||
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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('./dropdown-action-process.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'NO. RM' },
|
||||
{ label: 'NO. BILL' },
|
||||
{ label: 'JK' },
|
||||
{ label: 'ALAMAT' },
|
||||
{ label: 'KLINIK ASAL' },
|
||||
{ label: 'NAMA DOKTER' },
|
||||
{ label: 'CARA BAYAR' },
|
||||
{ label: 'RUJUKAN' },
|
||||
{ label: 'KET. RUJUKAN' },
|
||||
{ label: 'ASAL' },
|
||||
{ label: '' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'tanggal',
|
||||
'noRm',
|
||||
'noBill',
|
||||
'jk',
|
||||
'alamat',
|
||||
'klinik',
|
||||
'dokter',
|
||||
'caraBayar',
|
||||
'rujukan',
|
||||
'ketRujukan',
|
||||
'asal',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 60 },
|
||||
{ width: 200 },
|
||||
{ width: 100 },
|
||||
{ width: 100 },
|
||||
{ width: 150 },
|
||||
{ width: 80 },
|
||||
{ width: 200 },
|
||||
{ width: 120 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'NO.' },
|
||||
{ label: 'NAMA OBAT' },
|
||||
{ label: 'DOSIS' },
|
||||
{ label: 'SATUAN' },
|
||||
{ label: 'RUTE PEMBERIAN' },
|
||||
{ label: 'HARI' },
|
||||
{ label: 'CATATAN' },
|
||||
{ label: '' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'number',
|
||||
'namaObat',
|
||||
'dosis',
|
||||
'satuan',
|
||||
'rute',
|
||||
'hari',
|
||||
'catatan',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'NO.' },
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'SIKLUS' },
|
||||
{ label: 'PERIODE KEMOTERAPI' },
|
||||
{ label: 'KEHADIRAN' },
|
||||
{ label: '' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'number',
|
||||
'tanggal',
|
||||
'siklus',
|
||||
'periode',
|
||||
'kehadiran',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'NO. RM' },
|
||||
{ label: 'NO. BILL' },
|
||||
{ label: 'JK' },
|
||||
{ label: 'ALAMAT' },
|
||||
{ label: 'KLINIK ASAL' },
|
||||
{ label: 'NAMA DOKTER' },
|
||||
{ label: 'CARA BAYAR' },
|
||||
{ label: 'RUJUKAN' },
|
||||
{ label: 'KET. RUJUKAN' },
|
||||
{ label: 'ASAL' },
|
||||
{ label: '' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'tanggal',
|
||||
'noRm',
|
||||
'noBill',
|
||||
'jk',
|
||||
'alamat',
|
||||
'klinik',
|
||||
'dokter',
|
||||
'caraBayar',
|
||||
'rujukan',
|
||||
'ketRujukan',
|
||||
'asal',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||
const verifyButton = defineAsyncComponent(() => import('./verify-button.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 150 },
|
||||
{ width: 150 },
|
||||
{ width: 150 },
|
||||
{ width: 150 },
|
||||
{ width: 180 },
|
||||
{ width: 150 },
|
||||
{ width: 100 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL MASUK' },
|
||||
{ label: 'PJ BERKAS RM' },
|
||||
{ label: 'DOKTER' },
|
||||
{ label: 'JENIS RUANGAN' },
|
||||
{ label: 'JENIS TINDAKAN' },
|
||||
{ label: 'TANGGAL JADWAL TINDAKAN' },
|
||||
{ label: 'STATUS' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'tanggalMasuk',
|
||||
'pjBerkasRm',
|
||||
'dokter',
|
||||
'jenisRuangan',
|
||||
'jenisTindakan',
|
||||
'tanggalJadwalTindakan',
|
||||
'status',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as SmallDetailDto
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
status(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
return res
|
||||
},
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: verifyButton,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { format, parseISO } from 'date-fns'
|
||||
import { id as localeID } from 'date-fns/locale'
|
||||
|
||||
type VisitDto = any
|
||||
|
||||
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||
const verifyButton = defineAsyncComponent(() => import('./verify-button.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 150 }, // TANGGAL MASUK
|
||||
{ width: 180 }, // PJ BERKAS RM
|
||||
{ width: 200 }, // DOKTER
|
||||
{ width: 150 }, // JENIS RUANGAN
|
||||
{ width: 150 }, // JENIS TINDAKAN
|
||||
{ width: 180 }, // TANGGAL JADWAL TINDAKAN
|
||||
{ width: 150 }, // STATUS
|
||||
{ width: 120 }, // AKSI
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL MASUK' },
|
||||
{ label: 'PJ BERKAS RM' },
|
||||
{ label: 'DOKTER' },
|
||||
{ label: 'JENIS RUANGAN' },
|
||||
{ label: 'JENIS TINDAKAN' },
|
||||
{ label: 'TANGGAL JADWAL TINDAKAN' },
|
||||
{ label: 'STATUS' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'tanggal_masuk',
|
||||
'pj_berkas_rm',
|
||||
'dokter',
|
||||
'jenis_ruangan',
|
||||
'jenis_tindakan',
|
||||
'tanggal_jadwal_tindakan',
|
||||
'status',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'nama', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
tanggal_masuk: (rec: unknown): string => {
|
||||
const recX = rec as VisitDto
|
||||
if (!recX.tanggal_masuk) return '-'
|
||||
try {
|
||||
const date = typeof recX.tanggal_masuk === 'string' ? parseISO(recX.tanggal_masuk) : recX.tanggal_masuk
|
||||
return format(date, 'dd MMMM yyyy', { locale: localeID })
|
||||
} catch {
|
||||
return recX.tanggal_masuk.toString()
|
||||
}
|
||||
},
|
||||
tanggal_jadwal_tindakan: (rec: unknown): string => {
|
||||
const recX = rec as VisitDto
|
||||
if (!recX.tanggal_jadwal_tindakan) return '-'
|
||||
try {
|
||||
const date =
|
||||
typeof recX.tanggal_jadwal_tindakan === 'string'
|
||||
? parseISO(recX.tanggal_jadwal_tindakan)
|
||||
: recX.tanggal_jadwal_tindakan
|
||||
return format(date, 'dd MMMM yyyy', { locale: localeID })
|
||||
} catch {
|
||||
return recX.tanggal_jadwal_tindakan.toString()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
status(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
return res
|
||||
},
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: verifyButton,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<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 { config } from './list-cfg.verification'
|
||||
|
||||
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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<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 { config } from './list-cfg.medicine'
|
||||
|
||||
const searchQuery = ref('')
|
||||
|
||||
function handleSearch(event: Event) {
|
||||
const target = event.target as HTMLInputElement
|
||||
searchQuery.value = target.value
|
||||
// TODO: Implement search logic here
|
||||
// You can emit an event to parent or filter data directly
|
||||
}
|
||||
|
||||
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">
|
||||
<!-- Title and Search Section -->
|
||||
<div class="flex flex-col items-start">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div>
|
||||
<h2 class="mb-1 text-xl font-semibold">Protokol Obat Kemoterapi</h2>
|
||||
<p class="mb-4 text-sm text-gray-500">Daftar obat-obatan yang digunakan dalam protokol kemoterapi.</p>
|
||||
</div>
|
||||
<button class="rounded bg-orange-500 px-3 py-2 text-white hover:bg-orange-600">
|
||||
<i class="ri-add-line"></i>
|
||||
Tambah Obat Kemoterapi
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative mt-10 w-72">
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Cari obat..."
|
||||
class="w-full rounded-md border px-4 py-2 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
@input="handleSearch"
|
||||
/>
|
||||
<span class="absolute right-3 top-2.5 text-gray-400">
|
||||
<i class="ri-search-line"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<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 { config } from './list-cfg.protocol'
|
||||
|
||||
const searchQuery = ref('')
|
||||
|
||||
function handleSearch(event: Event) {
|
||||
const target = event.target as HTMLInputElement
|
||||
searchQuery.value = target.value
|
||||
// TODO: Implement search logic here
|
||||
// You can emit an event to parent or filter data directly
|
||||
}
|
||||
|
||||
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">
|
||||
<!-- Title and Search Section -->
|
||||
<div class="flex flex-col items-start">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div>
|
||||
<h2 class="mb-1 text-xl font-semibold">Protokol Kemoterapi</h2>
|
||||
<p class="mb-4 text-sm text-gray-500">Rangkaian prosedur kemoterapi yang terintegrasi dan konsisten.</p>
|
||||
</div>
|
||||
<button class="rounded bg-orange-500 px-3 py-2 text-white hover:bg-orange-600">
|
||||
<i class="ri-add-line"></i>
|
||||
Tambah Protokol Kemoterapi
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative mt-10 w-72">
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Cari protokol..."
|
||||
class="w-full rounded-md border px-4 py-2 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
@input="handleSearch"
|
||||
/>
|
||||
<span class="absolute right-3 top-2.5 text-gray-400">
|
||||
<i class="ri-search-line"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,71 +0,0 @@
|
||||
<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 { config } from './list-cfg.protocol'
|
||||
|
||||
const searchQuery = ref('')
|
||||
|
||||
function handleSearch(event: Event) {
|
||||
const target = event.target as HTMLInputElement
|
||||
searchQuery.value = target.value
|
||||
// TODO: Implement search logic here
|
||||
// You can emit an event to parent or filter data directly
|
||||
}
|
||||
|
||||
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">
|
||||
<!-- Title and Search Section -->
|
||||
<div class="flex flex-col items-start">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div>
|
||||
<h2 class="mb-1 text-xl font-semibold">Daftar Kunjungan Rawat Jalan Kemoterapi</h2>
|
||||
<p class="mb-4 text-sm text-gray-500">Manajemen pendaftaran serta monitoring terapi pasien tindakan rawat jalan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative mt-10 w-72">
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Cari jadwal pemeriksaan..."
|
||||
class="w-full rounded-md border px-4 py-2 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
@input="handleSearch"
|
||||
/>
|
||||
<span class="absolute right-3 top-2.5 text-gray-400">
|
||||
<i class="ri-search-line"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,45 +0,0 @@
|
||||
<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 { config } from './list-cfg.visit'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
verify: [rec: any]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
|
||||
// Provide verify handler to child components via provide/inject
|
||||
function handleVerify(rec: any) {
|
||||
emit('verify', rec)
|
||||
}
|
||||
|
||||
provide('verify-handler', handleVerify)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<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 { config } 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
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,49 +0,0 @@
|
||||
export type ChemotherapyData = {
|
||||
id: number
|
||||
tanggal: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ChemotherapyData[] = [
|
||||
{
|
||||
id: 1,
|
||||
tanggal: '12 Agustus 2025',
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tanggal: '11 Agustus 2025',
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
@@ -1,28 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Badge } from '~/components/pub/ui/badge'
|
||||
|
||||
const props = defineProps<{
|
||||
rec: any
|
||||
idx?: number
|
||||
}>()
|
||||
|
||||
const statusMap: Record<string, { text: string; variant: 'default' | 'secondary' | 'fresh' | 'positive' | 'negative' | 'warning' | 'destructive' | 'outline' }> = {
|
||||
'belum_terverifikasi': { text: 'Belum Terverifikasi', variant: 'secondary' },
|
||||
'terverifikasi': { text: 'Terverifikasi', variant: 'positive' },
|
||||
'ditolak': { text: 'Ditolak', variant: 'destructive' },
|
||||
}
|
||||
|
||||
const statusInfo = computed(() => {
|
||||
const status = props.rec.status?.toLowerCase() || props.rec.status_code?.toLowerCase() || 'belum_terverifikasi'
|
||||
return statusMap[status] || statusMap['belum_terverifikasi']
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">
|
||||
<Badge :variant="statusInfo.variant">
|
||||
{{ statusInfo.text }}
|
||||
</Badge>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Button from '~/components/pub/ui/button/Button.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
rec: any
|
||||
idx?: number
|
||||
}>()
|
||||
|
||||
// Try to get verify handler from parent via inject
|
||||
const verifyHandler = inject<(rec: any) => void>('verify-handler', null)
|
||||
|
||||
function handleVerify() {
|
||||
if (verifyHandler) {
|
||||
verifyHandler(props.rec)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
class="border-orange-500 bg-orange-50 text-orange-600 hover:bg-orange-100 hover:text-orange-700"
|
||||
@click="handleVerify"
|
||||
>
|
||||
Verifikasi
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
// Types
|
||||
import type { ConsultationFormData } from '~/schemas/consultation.schema.ts'
|
||||
|
||||
// Helpers
|
||||
import type z from 'zod'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import Textarea from '~/components/pub/ui/textarea/Textarea.vue'
|
||||
import type { CreateDto } from '~/models/consultation'
|
||||
|
||||
interface Props {
|
||||
schema: z.ZodSchema<any>
|
||||
values: CreateDto
|
||||
encounter_id: number
|
||||
units: { value: string; label: string }[]
|
||||
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: ConsultationFormData, resetForm: () => void]
|
||||
cancel: [resetForm: () => void]
|
||||
}>()
|
||||
|
||||
const today = new Date()
|
||||
const { defineField, errors, meta } = useForm({
|
||||
validationSchema: toTypedSchema(props.schema),
|
||||
initialValues: {
|
||||
date: props.values.date || today.toISOString().slice(0, 10),
|
||||
problem: '',
|
||||
dstUnit_code: '',
|
||||
} as ConsultationFormData,
|
||||
})
|
||||
|
||||
const [date, dateAttrs] = defineField('date')
|
||||
const [unit_code, unitAttrs] = defineField('unit_code')
|
||||
const [problem, problemAttrs] = defineField('problem')
|
||||
|
||||
// Fill fields from props.values if provided
|
||||
if (props.values) {
|
||||
if (props.values.date !== undefined) date.value = props.values.date.substring(0, 10)
|
||||
if (props.values.dstUnit_code !== undefined) unit_code.value = props.values.dstUnit_code
|
||||
if (props.values.problem !== undefined) problem.value = props.values.problem
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
date.value = date.value ?? today.toISOString().slice(0, 10)
|
||||
unit_code.value = 0
|
||||
problem.value = ''
|
||||
}
|
||||
|
||||
// Form submission handler
|
||||
function onSubmitForm(values: any) {
|
||||
const formData: ConsultationFormData = {
|
||||
encounter_id: props.encounter_id,
|
||||
date: date.value ? `${date.value}T00:00:00Z` : '',
|
||||
problem: problem.value || '',
|
||||
dstUnit_code: unit_code.value || '',
|
||||
}
|
||||
emit('submit', formData, resetForm)
|
||||
}
|
||||
|
||||
// Form cancel handler
|
||||
function onCancelForm() {
|
||||
emit('cancel', resetForm)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form id="form-division" @submit.prevent>
|
||||
<DE.Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="3" :cellFlex="false">
|
||||
<DE.Cell>
|
||||
<DE.Label>Tanggal</DE.Label>
|
||||
<DE.Field :errMessage="errors.code">
|
||||
<Input
|
||||
v-model.number="date"
|
||||
icon-name="i-lucide-chevron-down"
|
||||
v-bind="dateAttrs"
|
||||
readonly
|
||||
/>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label>Unit</DE.Label>
|
||||
<DE.Field :errMessage="errors.unit_code">
|
||||
{{ errors.unit_code }}
|
||||
<Select
|
||||
id="strUnit_code"
|
||||
v-model="unit_code"
|
||||
icon-name="i-lucide-chevron-down"
|
||||
placeholder="Pilih poliklinik tujuan"
|
||||
v-bind="unitAttrs"
|
||||
:items="props.units || []"
|
||||
:disabled="isLoading || isReadonly"
|
||||
/>
|
||||
<!-- <Input type="number" id="unit_code" v-model.number="unit_code" v-bind="unitAttrs" :disabled="isLoading || isReadonly" /> -->
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell :colSpan="3">
|
||||
<DE.Label>Uraian</DE.Label>
|
||||
<DE.Field :errMessage="errors.problem">
|
||||
<Textarea id="problem" v-model="problem" v-bind="problemAttrs" :disabled="isLoading || isReadonly" />
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.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>
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { Config, RecComponent, RecStrFuncComponent, RecStrFuncUnknown } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import type { Consultation } from '~/models/consultation'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
|
||||
export const config: Config = {
|
||||
cols: [{ width: 100 }, {}, {}, {}, { width: 50 }],
|
||||
headers: [[
|
||||
{ label: 'Tanggal' },
|
||||
{ label: 'Tujuan' },
|
||||
{ label: 'Dokter' },
|
||||
{ label: 'Pertanyaan' },
|
||||
{ label: 'Jawaban' },
|
||||
{ label: '' },
|
||||
]],
|
||||
keys: ['date', 'dstUnit.name', 'dstDoctor.name', 'problem', 'solution', 'action'],
|
||||
delKeyNames: [
|
||||
{ key: 'data', label: 'Tanggal' },
|
||||
{ key: 'dstDoctor.name', label: 'Dokter' },
|
||||
],
|
||||
parses: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
props: {
|
||||
size: 'sm',
|
||||
},
|
||||
}
|
||||
return res
|
||||
},
|
||||
date(rec) {
|
||||
const recX = rec as Consultation
|
||||
return recX.date?.substring(0, 10) || '-'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
props: {
|
||||
size: 'sm',
|
||||
},
|
||||
}
|
||||
return res
|
||||
},
|
||||
} as RecStrFuncComponent,
|
||||
htmls: {} as RecStrFuncUnknown,
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<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 { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<!-- FIXME: pindahkan ke content/division/list.vue -->
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,116 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'birthDate',
|
||||
label = 'Tanggal Lahir',
|
||||
placeholder = 'Pilih tanggal lahir',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
// Reactive variables for age calculation
|
||||
const patientAge = ref<string>('Masukkan tanggal lahir')
|
||||
|
||||
// Function to calculate age with years, months, and days
|
||||
function calculateAge(birthDate: string | Date | undefined): string {
|
||||
if (!birthDate) {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
|
||||
try {
|
||||
let dateObj: Date
|
||||
|
||||
if (typeof birthDate === 'string') {
|
||||
dateObj = parseISO(birthDate)
|
||||
} else {
|
||||
dateObj = birthDate
|
||||
}
|
||||
|
||||
const today = new Date()
|
||||
|
||||
// Calculate years, months, and days
|
||||
const totalYears = differenceInYears(today, dateObj)
|
||||
|
||||
// Calculate remaining months after years
|
||||
const yearsPassed = new Date(dateObj)
|
||||
yearsPassed.setFullYear(yearsPassed.getFullYear() + totalYears)
|
||||
const remainingMonths = differenceInMonths(today, yearsPassed)
|
||||
|
||||
// Calculate remaining days after years and months
|
||||
const monthsPassed = new Date(yearsPassed)
|
||||
monthsPassed.setMonth(monthsPassed.getMonth() + remainingMonths)
|
||||
const remainingDays = differenceInDays(today, monthsPassed)
|
||||
|
||||
// Format the result
|
||||
const parts = []
|
||||
if (totalYears > 0) parts.push(`${totalYears} Tahun`)
|
||||
if (remainingMonths > 0) parts.push(`${remainingMonths} Bulan`)
|
||||
if (remainingDays > 0) parts.push(`${remainingDays} Hari`)
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : '0 Hari'
|
||||
} catch {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="birthDate"
|
||||
type="date"
|
||||
min="1900-01-01"
|
||||
v-bind="componentField"
|
||||
:placeholder="placeholder"
|
||||
@update:model-value="
|
||||
(value: string | number) => {
|
||||
const dateStr = typeof value === 'number' ? String(value) : value
|
||||
patientAge = calculateAge(dateStr)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,98 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const doctors = ref<Array<Item>>([])
|
||||
|
||||
async function fetchDpjp(specialistId: string, subspecialistId: string) {
|
||||
doctors.value = await getDoctorLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
includes: 'employee-person',
|
||||
// "unit-id": parseInt(unitId),
|
||||
"specialist-code": String(specialistId),
|
||||
"subspecialist-code": String(subspecialistId),
|
||||
}, true)
|
||||
}
|
||||
|
||||
// const selectedUnitId = inject<Ref<string | null>>("selectedUnitId")!
|
||||
const selectedSpecialistId = inject<Ref<string | null>>("selectedSpecialistId")!
|
||||
const selectedSubSpecialistId = inject<Ref<string | null>>("selectedSubSpecialistId")!
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
|
||||
watch([ selectedSpecialistId, selectedSubSpecialistId], () => {
|
||||
if (selectedSpecialistId.value && selectedSubSpecialistId.value) {
|
||||
console.log(`Select Doctor`)
|
||||
fetchDpjp( selectedSpecialistId.value, selectedSubSpecialistId.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="doctors"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
:is-disabled="selectedSubSpecialistId === null"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,98 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
import { getValueLabelList as getSpecialistLabelList } from '~/services/specialist.service'
|
||||
import { getValueLabelList as getSubspecialistLabelList } from '~/services/subspecialist.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const specialists = ref<Array<Item>>([])
|
||||
|
||||
async function fetchSpecialists(unitId: string) {
|
||||
specialists.value = await getSpecialistLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
specialistCode: 0,
|
||||
"unit-id": String(unitId),
|
||||
}, true)
|
||||
}
|
||||
|
||||
const selectedUnitId = inject<Ref<string | null>>("selectedUnitId")!
|
||||
const selectedSpecialistId = inject<Ref<string | null>>("selectedSpecialistId")!
|
||||
|
||||
function handleSpecialistChange(selected: string) {
|
||||
selectedSpecialistId.value = selected ?? null
|
||||
}
|
||||
|
||||
watch([selectedUnitId], () => {
|
||||
if (selectedUnitId.value) {
|
||||
fetchSpecialists(selectedUnitId.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Block :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<div>
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
Spesialis
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="specialists"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
@update:model-value="handleSpecialistChange"
|
||||
:is-disabled="selectedUnitId === null"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</div>
|
||||
</DE.Block>
|
||||
</template>
|
||||
@@ -1,97 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
import { getValueLabelList as getSubspecialistLabelList } from '~/services/subspecialist.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const subspecialists = ref<Array<Item>>([])
|
||||
|
||||
async function fetchSubSpecialists(specialistId: string) {
|
||||
subspecialists.value = await getSubspecialistLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
specialistCode: 0,
|
||||
"specialist-code": String(specialistId),
|
||||
}, true)
|
||||
}
|
||||
|
||||
const selectedSpecialistId = inject<Ref<string | null>>("selectedSpecialistId")!
|
||||
const selectedSubSpecialistId = inject<Ref<string | null>>("selectedSubSpecialistId")!
|
||||
|
||||
function handleSubSpecialistChange(selected: string) {
|
||||
selectedSubSpecialistId.value = selected ?? null
|
||||
}
|
||||
|
||||
watch([selectedSpecialistId], () => {
|
||||
if (selectedSpecialistId.value) {
|
||||
fetchSubSpecialists(selectedSpecialistId.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Block :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<div>
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
Sub Spesialis
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="subspecialists"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
@update:model-value="handleSubSpecialistChange"
|
||||
:is-disabled="selectedSpecialistId === null"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</div>
|
||||
</DE.Block>
|
||||
</template>
|
||||
@@ -1,85 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { occupationCodes } from '~/lib/constants'
|
||||
import { getValueLabelList as getUnitLabelList } from '~/services/unit.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const units = ref<Array<Item>>([])
|
||||
|
||||
async function fetchData() {
|
||||
units.value = await getUnitLabelList({})
|
||||
}
|
||||
|
||||
const selectedUnitId = inject<Ref<string | null>>("selectedUnitId")!
|
||||
function handleDataChange(selected: string) {
|
||||
selectedUnitId.value = selected ?? null
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:class="cn('select-field-label', labelClass)"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="units"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
@update:model-value="handleDataChange"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -1,94 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import SelectDate from './_common/select-date.vue'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import SelectSpeciality from './_common/select-specialist.vue'
|
||||
import SelectDpjp from './_common/select-dpjp.vue'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import SelectUnit from './_common/select-unit.vue'
|
||||
import SelectSubspecialist from './_common/select-subspecialist.vue'
|
||||
import SelectSpecialist from './_common/select-specialist.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
schema: any
|
||||
initialValues?: any
|
||||
errors?: FormErrors
|
||||
|
||||
selectedUnitId?: number | null
|
||||
selectedSpecialistId?: number | null
|
||||
selectedSubSpecialistId?: number | null
|
||||
}>()
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ? initialValues : {}"
|
||||
>
|
||||
<DE.Block :col-count="2" :cell-flex="false">
|
||||
<InputBase
|
||||
field-name="sepStatus"
|
||||
label="Status Sep"
|
||||
placeholder="Status Sep"
|
||||
:is-disabled="true"
|
||||
/>
|
||||
<SelectDate
|
||||
field-name="date"
|
||||
label="Tanggal Rencana Kontrol"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<DE.Cell :col-span="2">
|
||||
<DE.Block :col-count="4" :cell-flex="false">
|
||||
<SelectUnit
|
||||
field-name="unit_code"
|
||||
label="Unit"
|
||||
placeholder="Pilih Unit"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectSpecialist
|
||||
field-name="specialist_code"
|
||||
label="Spesialis/Sub Spesialis"
|
||||
placeholder="Pilih Spesialis/Sub Spesialis"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectSubspecialist
|
||||
field-name="subspecialist_code"
|
||||
label="Spesialis/Sub Spesialis"
|
||||
placeholder="Pilih Spesialis/Sub Spesialis"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectDpjp
|
||||
field-name="doctor_code"
|
||||
label="DPJP"
|
||||
placeholder="Pilih DPJP"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
</DE.Block>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -1,28 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
import { config } from './history-list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const isModalOpen = inject(`isHistoryDialogOpen`) as Ref<boolean>
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="isModalOpen" title="Riwayat Surat Kontrol" size="full">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="props.paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -1,85 +0,0 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { educationCodes, genderCodes } from '~/lib/constants'
|
||||
import { calculateAge } from '~/lib/utils'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/print-btn.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {width: 130}, {width: 30},],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'NAMA PASIEN' },
|
||||
{ label: 'NO.SURAT KONTROL' },
|
||||
{ label: 'NO.SEP' },
|
||||
{ label: 'TANGGAL RENCANA KONTROL' },
|
||||
{ label: 'TANGGAL TERBIT' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'SPESIALIS' },
|
||||
{ label: 'SUBSPESIALIS' },
|
||||
{ label: 'TIPE RAWAT' },
|
||||
{ label: 'DIBUAT OLEH' },
|
||||
{ label: 'DIEDIT OLEH' },
|
||||
{ label: 'STATUS' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'date',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'name',
|
||||
'status',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
date: (rec: unknown): unknown => {
|
||||
const date = (rec as any).date
|
||||
if (typeof date == 'object' && date) {
|
||||
return (date as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof date == 'string') {
|
||||
return (date as string).substring(0, 10)
|
||||
}
|
||||
return date
|
||||
},
|
||||
specialist_subspecialist: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
// const { person } = rec as Patient
|
||||
return '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
sep_status(_rec) {
|
||||
return 'SEP Internal'
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { educationCodes, genderCodes } from '~/lib/constants'
|
||||
import { calculateAge } from '~/lib/utils'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{width: 180}, {}, {}, {}, {}, {width: 30},],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Tgl Rencana Kontrol' },
|
||||
{ label: 'Spesialis' },
|
||||
{ label: 'Sub Spesialis' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'Status SEP' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'specialist.name', 'subspecialist.name', 'doctor.employee.person.name', 'sep_status', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
date: (rec: unknown): unknown => {
|
||||
const date = (rec as any).date
|
||||
if (typeof date == 'object' && date) {
|
||||
return (date as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof date == 'string') {
|
||||
return (date as string).substring(0, 10)
|
||||
}
|
||||
return date
|
||||
},
|
||||
specialist_subspecialist: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
// const { person } = rec as Patient
|
||||
return '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
sep_status(_rec) {
|
||||
return 'SEP Internal'
|
||||
},
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user