Compare commits
12 Commits
nico/30-ja
...
nico/11-fe
| Author | SHA1 | Date | |
|---|---|---|---|
| 9678e6979b | |||
| b35874b120 | |||
| b69df2454e | |||
| df198c320a | |||
| f550e29a75 | |||
| bb7384f1e5 | |||
| df154806f7 | |||
| 25000d0b0f | |||
| bbd52fb6f5 | |||
| 358ff14efe | |||
| 6c36a15290 | |||
| da585dde99 |
65
.gemini/hooks/telegram-notify.ts
Executable file
65
.gemini/hooks/telegram-notify.ts
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bun
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
// Fungsi untuk mencari string terpanjang dalam objek (biasanya balasan AI)
|
||||
function findLongestString(obj: any): string {
|
||||
let longest = "";
|
||||
const search = (item: any) => {
|
||||
if (typeof item === "string") {
|
||||
if (item.length > longest.length) longest = item;
|
||||
} else if (Array.isArray(item)) {
|
||||
item.forEach(search);
|
||||
} else if (item && typeof item === "object") {
|
||||
Object.values(item).forEach(search);
|
||||
}
|
||||
};
|
||||
search(obj);
|
||||
return longest;
|
||||
}
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const inputRaw = readFileSync(0, "utf-8");
|
||||
if (!inputRaw) return;
|
||||
const input = JSON.parse(inputRaw);
|
||||
|
||||
// DEBUG: Lihat struktur asli di console terminal (stderr)
|
||||
console.error("DEBUG KEYS:", Object.keys(input));
|
||||
|
||||
const BOT_TOKEN = process.env.BOT_TOKEN;
|
||||
const CHAT_ID = process.env.CHAT_ID;
|
||||
|
||||
const sessionId = input.session_id || "unknown";
|
||||
|
||||
// Cari teks secara otomatis di seluruh objek JSON
|
||||
let finalText = findLongestString(input.response || input);
|
||||
|
||||
if (!finalText || finalText.length < 5) {
|
||||
finalText =
|
||||
"Teks masih gagal diekstraksi. Struktur: " +
|
||||
Object.keys(input).join(", ");
|
||||
}
|
||||
|
||||
const message =
|
||||
`✅ *Gemini Task Selesai*\n\n` +
|
||||
`🆔 Session: \`${sessionId}\` \n\n` +
|
||||
`🧠 Output:\n${finalText.substring(0, 3500)}`;
|
||||
|
||||
await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
chat_id: CHAT_ID,
|
||||
text: message,
|
||||
parse_mode: "Markdown",
|
||||
}),
|
||||
});
|
||||
|
||||
process.stdout.write(JSON.stringify({ status: "continue" }));
|
||||
} catch (err) {
|
||||
console.error("Hook Error:", err);
|
||||
process.stdout.write(JSON.stringify({ status: "continue" }));
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
17
.gemini/settings.json
Normal file
17
.gemini/settings.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"hooks": {
|
||||
"AfterAgent": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"name": "telegram-notify",
|
||||
"type": "command",
|
||||
"command": "bun $GEMINI_PROJECT_DIR/.gemini/hooks/telegram-notify.ts",
|
||||
"timeout": 10000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
62
GEMINI.md
Normal file
62
GEMINI.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Project: Desa Darmasaba
|
||||
|
||||
## Project Overview
|
||||
|
||||
The `desa-darmasaba` project is a Next.js (version 15+) application developed with TypeScript. It serves as an official platform for Desa Darmasaba (a village in Badung, Bali), offering various public services, news, and detailed village profiles.
|
||||
|
||||
**Key Technologies:**
|
||||
|
||||
* **Frontend Framework:** Next.js (v15+) with React (v19+)
|
||||
* **Language:** TypeScript
|
||||
* **UI Library:** Mantine UI
|
||||
* **Database ORM:** Prisma (v6+)
|
||||
* **Database:** PostgreSQL (as configured in `prisma/schema.prisma`)
|
||||
* **API Framework:** Elysia (used for API routes, as seen in dependencies)
|
||||
* **State Management:** Potentially Jotai and Valtio (listed in dependencies)
|
||||
* **Image Processing:** Sharp
|
||||
* **Package Manager:** Likely Bun, given `bun.lockb` and the `prisma:seed` script.
|
||||
|
||||
The application architecture follows the Next.js App Router structure, with comprehensive data models defined in `prisma/schema.prisma` covering various domains like public information, health, security, economy, innovation, environment, and education. It also includes configurations for image handling and caching.
|
||||
|
||||
## Building and Running
|
||||
|
||||
This project uses `bun` as the package manager. Ensure Bun is installed to run these commands.
|
||||
|
||||
* **Install Dependencies:**
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
* **Development Server:**
|
||||
Runs the Next.js development server.
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
* **Build for Production:**
|
||||
Builds the Next.js application for production deployment.
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
|
||||
* **Start Production Server:**
|
||||
Starts the Next.js application in production mode.
|
||||
```bash
|
||||
bun run start
|
||||
```
|
||||
|
||||
* **Database Seeding:**
|
||||
Executes the Prisma seeding script to populate the database.
|
||||
```bash
|
||||
bun run prisma:seed
|
||||
```
|
||||
|
||||
## Development Conventions
|
||||
|
||||
* **Coding Language:** TypeScript is strictly enforced.
|
||||
* **Frontend Framework:** Next.js App Router for page and component structuring.
|
||||
* **UI/UX:** Adherence to Mantine UI component library for consistent styling and user experience.
|
||||
* **Database Interaction:** Prisma ORM is used for all database operations, with a PostgreSQL database.
|
||||
* **Linting:** ESLint is configured with `next/core-web-vitals` and `next/typescript` to maintain code quality and adherence to Next.js and TypeScript best practices.
|
||||
* **Styling:** PostCSS is used, with `postcss-preset-mantine` and `postcss-simple-vars` defining Mantine-specific breakpoints and other CSS variables.
|
||||
* **Imports:** Absolute imports are configured using `@/*` which resolves to the `src/` directory.
|
||||
305
QWEN.md
305
QWEN.md
@@ -2,7 +2,7 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
Desa Darmasaba is a comprehensive Next.js 15 application designed for village management services in Badung, Bali. The platform serves as a digital hub for government services, public information, news, and community engagement for the residents of Darmasaba village.
|
||||
Desa Darmasaba is a comprehensive Next.js 15 application designed for village management services in Darmasaba, Badung, Bali. The application serves as a digital platform for government services, public information, and community engagement. It features multiple sections including PPID (Public Information Disclosure), health services, security, education, environment, economy, innovation, and more.
|
||||
|
||||
### Key Technologies
|
||||
- **Framework**: Next.js 15 with App Router
|
||||
@@ -10,146 +10,104 @@ Desa Darmasaba is a comprehensive Next.js 15 application designed for village ma
|
||||
- **Styling**: Mantine UI components with custom CSS
|
||||
- **Backend**: Elysia.js API server integrated with Next.js
|
||||
- **Database**: PostgreSQL with Prisma ORM
|
||||
- **State Management**: Jotai for global state
|
||||
- **State Management**: Valtio for global state
|
||||
- **Authentication**: JWT with iron-session
|
||||
- **Runtime**: Bun (instead of Node.js)
|
||||
|
||||
### Architecture
|
||||
The application follows a modern full-stack architecture with:
|
||||
- Frontend built with Next.js 15 and TypeScript
|
||||
- Backend API endpoints using Elysia.js
|
||||
- PostgreSQL database managed with Prisma ORM
|
||||
- Mantine UI library for consistent design components
|
||||
- File storage system for managing images and documents
|
||||
- Comprehensive user authentication and authorization system
|
||||
|
||||
## Features
|
||||
|
||||
The application provides extensive functionality across multiple domains:
|
||||
|
||||
### Government Services
|
||||
- Public service information and requests
|
||||
- Administrative online services
|
||||
- Community announcements and news
|
||||
- Village profile and governance information
|
||||
|
||||
### Health Services
|
||||
- Healthcare facility information
|
||||
- Health programs and initiatives
|
||||
- Emergency health contacts
|
||||
- Health articles and education
|
||||
- Posyandu (community health posts) information
|
||||
|
||||
### Economic Development
|
||||
- Local market information
|
||||
- Job listings and employment opportunities
|
||||
- Village economic statistics
|
||||
- Business registration and support
|
||||
|
||||
### Innovation & Technology
|
||||
- Digital village initiatives
|
||||
- Technology adoption programs
|
||||
- Innovation proposal system
|
||||
- Smart village features
|
||||
|
||||
### Education
|
||||
- School information and statistics
|
||||
- Scholarship programs
|
||||
- Educational support services
|
||||
- Library and learning resources
|
||||
|
||||
### Environmental Management
|
||||
- Waste management systems
|
||||
- Green initiatives
|
||||
- Environmental conservation programs
|
||||
- Community participation activities
|
||||
|
||||
### Security & Safety
|
||||
- Emergency contacts
|
||||
- Police station information
|
||||
- Crime prevention measures
|
||||
- Community safety programs
|
||||
The application follows a modular architecture with:
|
||||
- A main frontend built with Next.js and Mantine UI
|
||||
- An integrated Elysia.js API server for backend operations
|
||||
- Prisma ORM for database interactions
|
||||
- File storage integration with Seafile
|
||||
- Multiple domain-specific modules (PPID, health, security, education, etc.)
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Prerequisites
|
||||
- Bun runtime (version specified in package.json)
|
||||
- Node.js (with Bun runtime)
|
||||
- PostgreSQL database
|
||||
- Environment variables configured
|
||||
- Seafile server for file storage
|
||||
|
||||
### Setup Commands
|
||||
```bash
|
||||
# Install dependencies
|
||||
bun install
|
||||
### Setup Instructions
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
# Set up environment variables
|
||||
cp .env.example .env.local
|
||||
# Edit .env.local with your configuration
|
||||
2. Set up environment variables in `.env.local`:
|
||||
```
|
||||
DATABASE_URL=your_postgresql_connection_string
|
||||
SEAFILE_TOKEN=your_seafile_token
|
||||
SEAFILE_REPO_ID=your_seafile_repo_id
|
||||
SEAFILE_BASE_URL=your_seafile_base_url
|
||||
SEAFILE_PUBLIC_SHARE_TOKEN=your_seafile_public_share_token
|
||||
SEAFILE_URL=your_seafile_api_url
|
||||
WIBU_UPLOAD_DIR=your_upload_directory
|
||||
```
|
||||
|
||||
# Database setup
|
||||
bunx prisma db push
|
||||
bunx prisma generate
|
||||
3. Generate Prisma client:
|
||||
```bash
|
||||
bunx prisma generate
|
||||
```
|
||||
|
||||
# Seed database (optional)
|
||||
bun run prisma/seed.ts
|
||||
4. Push database schema:
|
||||
```bash
|
||||
bunx prisma db push
|
||||
```
|
||||
|
||||
# Development mode
|
||||
bun run dev
|
||||
5. Seed the database:
|
||||
```bash
|
||||
bun run prisma/seed.ts
|
||||
```
|
||||
|
||||
# Production build
|
||||
bun run build
|
||||
6. Run the development server:
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
# Start production server
|
||||
bun run start
|
||||
```
|
||||
### Available Scripts
|
||||
- `bun run dev` - Start development server
|
||||
- `bun run build` - Build for production
|
||||
- `bun run start` - Start production server
|
||||
- `bun run prisma/seed.ts` - Run database seeding
|
||||
- `bunx prisma generate` - Generate Prisma client
|
||||
- `bunx prisma db push` - Push schema changes to database
|
||||
- `bunx prisma studio` - Open Prisma Studio GUI
|
||||
|
||||
### Additional Commands
|
||||
```bash
|
||||
# Linting (ESLint)
|
||||
bunx eslint .
|
||||
|
||||
# Type checking
|
||||
bunx tsc --noEmit
|
||||
|
||||
# Prisma operations
|
||||
bunx prisma generate
|
||||
bunx prisma db push
|
||||
bunx prisma studio
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
## Development Conventions
|
||||
|
||||
### Code Structure
|
||||
```
|
||||
src/
|
||||
├── app/ # Next.js app router pages
|
||||
│ ├── _com/ # Common components
|
||||
│ ├── admin/ # Admin panel routes
|
||||
│ ├── api/ # API routes
|
||||
│ ├── darmasaba/ # Main application pages
|
||||
│ ├── login/ # Authentication pages
|
||||
│ └── ... # Other feature pages
|
||||
├── con/ # Constants and static data
|
||||
├── hooks/ # Custom React hooks
|
||||
├── lib/ # Utility functions and configurations
|
||||
├── middlewares/ # Middleware functions
|
||||
├── state/ # Global state management
|
||||
├── store/ # Additional state management
|
||||
├── types/ # TypeScript type definitions
|
||||
├── utils/ # Utility functions
|
||||
├── middleware.ts # Application middleware
|
||||
└── schema.ts # Schema definitions
|
||||
│ ├── admin/ # Admin dashboard pages
|
||||
│ ├── api/ # API routes with Elysia.js
|
||||
│ ├── darmasaba/ # Public-facing village pages
|
||||
│ └── ...
|
||||
├── con/ # Constants and configuration
|
||||
├── hooks/ # React hooks
|
||||
├── lib/ # Utility functions and configurations
|
||||
├── middlewares/ # Next.js middleware
|
||||
├── state/ # Global state management
|
||||
├── store/ # Additional state management
|
||||
├── types/ # TypeScript type definitions
|
||||
└── utils/ # Utility functions
|
||||
```
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Code Style
|
||||
### Import Conventions
|
||||
- Use absolute imports with `@/` alias (configured in tsconfig.json)
|
||||
- Group imports: external libraries first, then internal modules
|
||||
- Follow consistent naming conventions:
|
||||
- Components: PascalCase (e.g., `UploadImage.tsx`)
|
||||
- Files: kebab-case for utilities (e.g., `api-fetch.ts`)
|
||||
- Variables/Functions: camelCase
|
||||
- Constants: UPPER_SNAKE_CASE
|
||||
- Keep import statements organized and remove unused imports
|
||||
|
||||
```typescript
|
||||
// External libraries
|
||||
import { useState } from 'react'
|
||||
import { Button, Stack } from '@mantine/core'
|
||||
|
||||
// Internal modules
|
||||
import ApiFetch from '@/lib/api-fetch'
|
||||
import { MyComponent } from '@/components/my-component'
|
||||
```
|
||||
|
||||
### TypeScript Configuration
|
||||
- Strict mode enabled (`"strict": true`)
|
||||
@@ -157,12 +115,52 @@ src/
|
||||
- Module resolution: bundler
|
||||
- Path alias: `@/*` maps to `./src/*`
|
||||
|
||||
### Naming Conventions
|
||||
- **Components**: PascalCase (e.g., `UploadImage.tsx`)
|
||||
- **Files**: kebab-case for utilities (e.g., `api-fetch.ts`)
|
||||
- **Variables/Functions**: camelCase
|
||||
- **Constants**: UPPER_SNAKE_CASE
|
||||
- **Database Models**: PascalCase (Prisma convention)
|
||||
|
||||
### Error Handling
|
||||
- Use try-catch blocks for async operations
|
||||
- Implement proper error boundaries in React components
|
||||
- Log errors appropriately without exposing sensitive data
|
||||
- Use Zod for runtime validation and type safety
|
||||
|
||||
### API Structure
|
||||
- Backend uses Elysia.js with TypeScript
|
||||
- API routes are in `src/app/api/[[...slugs]]/` directory
|
||||
- Use treaty client for type-safe API calls
|
||||
- Follow RESTful conventions for endpoints
|
||||
- Include proper HTTP status codes and error responses
|
||||
|
||||
### Database Operations
|
||||
- Use Prisma client from `@/lib/prisma.ts`
|
||||
- Database connection includes graceful shutdown handling
|
||||
- Use transactions for complex operations
|
||||
- Implement proper error handling for database queries
|
||||
|
||||
### Component Guidelines
|
||||
- Use functional components with hooks
|
||||
- Implement proper prop types with TypeScript interfaces
|
||||
- Use Mantine components for UI consistency
|
||||
- Follow atomic design principles when possible
|
||||
- Add loading states and error states for async operations
|
||||
|
||||
### State Management
|
||||
- Use Valtio proxies for global state
|
||||
- Keep local state in components when possible
|
||||
- Use SWR for server state caching
|
||||
- Implement optimistic updates for better UX
|
||||
|
||||
### Styling
|
||||
- Primary: Mantine UI components
|
||||
- Use Mantine theme system for customization
|
||||
- Custom CSS should be minimal and scoped
|
||||
- Follow responsive design principles
|
||||
- Use semantic HTML5 elements
|
||||
|
||||
### Security Practices
|
||||
- Validate all user inputs with Zod schemas
|
||||
- Use JWT tokens for authentication
|
||||
@@ -178,42 +176,57 @@ src/
|
||||
- Optimize bundle size with dynamic imports
|
||||
- Use Prisma query optimization
|
||||
|
||||
## Database Schema
|
||||
## Domain Modules
|
||||
|
||||
The application uses a comprehensive PostgreSQL database schema with Prisma ORM, featuring:
|
||||
The application is organized into several domain modules:
|
||||
|
||||
- **User Management**: Complete user authentication system with roles and permissions
|
||||
- **Content Management**: News, announcements, and various content types
|
||||
- **Service Management**: Various government and community services
|
||||
- **File Storage**: Centralized file management system
|
||||
- **Health Information**: Healthcare facilities and programs
|
||||
- **Economic Data**: Market information and employment data
|
||||
- **Educational Resources**: Schools, scholarships, and educational programs
|
||||
- **Environmental Data**: Sustainability and environmental management
|
||||
- **Security Information**: Emergency contacts and safety measures
|
||||
1. **PPID (Public Information Disclosure)**: Profile, structure, information requests, legal basis
|
||||
2. **Health**: Health facilities, programs, emergency response, disease information
|
||||
3. **Security**: Community security, emergency contacts, crime prevention
|
||||
4. **Education**: Schools, scholarships, educational programs
|
||||
5. **Economy**: Local markets, BUMDes, employment data
|
||||
6. **Environment**: Environmental data, conservation, waste management
|
||||
7. **Innovation**: Digital services, innovation programs
|
||||
8. **Culture**: Village traditions, music, cultural preservation
|
||||
|
||||
## API Structure
|
||||
Each module has its own section in both the admin panel and public-facing areas.
|
||||
|
||||
- Backend uses Elysia.js with TypeScript
|
||||
- API routes are in `src/app/api/[[...slugs]]/` directory
|
||||
- Use treaty client for type-safe API calls
|
||||
- Follow RESTful conventions for endpoints
|
||||
- Include proper HTTP status codes and error responses
|
||||
## File Storage Integration
|
||||
|
||||
The application integrates with Seafile for file storage, with specific handling for:
|
||||
- Images and documents
|
||||
- Public sharing capabilities
|
||||
- CDN URL generation
|
||||
- Batch processing of assets
|
||||
|
||||
## Testing
|
||||
|
||||
Currently no formal test framework is configured. When adding tests:
|
||||
- Consider Jest or Vitest for unit testing
|
||||
- Use Playwright for E2E testing
|
||||
- Update this section with specific test commands
|
||||
|
||||
## Deployment
|
||||
|
||||
The application includes deployment scripts for staging environments, with:
|
||||
- Automated builds using Bun
|
||||
- Database migration handling
|
||||
The application includes deployment scripts in the `NOTE.md` file that outline:
|
||||
- Automated deployment with GitHub API integration
|
||||
- Environment-specific configurations
|
||||
- PM2 process management for production
|
||||
- PM2 process management
|
||||
- Release management with versioning
|
||||
|
||||
## Important Notes
|
||||
## Troubleshooting
|
||||
|
||||
- The application uses a custom Elysia.js server integrated with Next.js API routes
|
||||
- Image uploads are handled through `/api/upl-img-single` endpoint
|
||||
- Database seeding is done with Bun runtime
|
||||
- The app supports Indonesian locale (id_ID) for SEO and content
|
||||
- CORS is configured to allow cross-origin requests during development
|
||||
- Authentication is implemented with JWT tokens and iron-session
|
||||
- The application has both public-facing and admin sections with different access controls
|
||||
Common issues and solutions:
|
||||
- **API endpoints returning 404**: Check that environment variables are properly configured
|
||||
- **Database connection errors**: Verify DATABASE_URL in environment variables
|
||||
- **File upload issues**: Ensure Seafile integration is properly configured
|
||||
- **Build failures**: Run `bunx prisma generate` before building
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. Always run type checking before committing: `bunx tsc --noEmit`
|
||||
2. Run linting to catch style issues: `bun run eslint .`
|
||||
3. Test database changes with `bunx prisma db push`
|
||||
4. Use the integrated Swagger docs at `/api/docs` for API testing
|
||||
5. Check environment variables are properly configured
|
||||
6. Verify responsive design on different screen sizes
|
||||
30
__tests__/api/fileStorage.test.ts
Normal file
30
__tests__/api/fileStorage.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
|
||||
describe('FileStorage API', () => {
|
||||
it('should fetch a list of files from /api/fileStorage/findMany', async () => {
|
||||
const response = await ApiFetch.api.fileStorage.findMany.get();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const responseBody = response.data as any;
|
||||
|
||||
expect(responseBody.data).toBeInstanceOf(Array);
|
||||
expect(responseBody.data.length).toBe(2);
|
||||
expect(responseBody.data[0].name).toBe('file1.jpg');
|
||||
});
|
||||
|
||||
it('should create a file using /api/fileStorage/create', async () => {
|
||||
const mockFile = new File(['hello'], 'hello.png', { type: 'image/png' });
|
||||
const response = await ApiFetch.api.fileStorage.create.post({
|
||||
file: mockFile,
|
||||
name: 'hello.png',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
const responseBody = response.data as any;
|
||||
|
||||
expect(responseBody.data.realName).toBe('hello.png');
|
||||
expect(responseBody.data.id).toBe('3');
|
||||
});
|
||||
});
|
||||
11
__tests__/e2e/homepage.spec.ts
Normal file
11
__tests__/e2e/homepage.spec.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('homepage has correct title and content', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Wait for the redirect to /darmasaba
|
||||
await page.waitForURL('/darmasaba');
|
||||
|
||||
// Check for the main heading
|
||||
await expect(page.getByText('DARMASABA', { exact: true })).toBeVisible();
|
||||
});
|
||||
43
__tests__/mocks/handlers.ts
Normal file
43
__tests__/mocks/handlers.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { http, HttpResponse } from 'msw';
|
||||
|
||||
export const handlers = [
|
||||
http.get('http://localhost:3000/api/fileStorage/findMany', () => {
|
||||
return HttpResponse.json({
|
||||
data: [
|
||||
{ id: '1', name: 'file1.jpg', url: '/uploads/file1.jpg' },
|
||||
{ id: '2', name: 'file2.png', url: '/uploads/file2.png' },
|
||||
],
|
||||
meta: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 2,
|
||||
totalPages: 1,
|
||||
},
|
||||
});
|
||||
}),
|
||||
http.post('http://localhost:3000/api/fileStorage/create', async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
const file = data.get('file') as File;
|
||||
const name = data.get('name') as string;
|
||||
|
||||
if (!file) {
|
||||
return new HttpResponse(null, { status: 400 });
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
id: '3',
|
||||
name: 'generated-nanoid',
|
||||
path: `/uploads/generated-nanoid`,
|
||||
link: `/uploads/generated-nanoid`,
|
||||
realName: name,
|
||||
mimeType: file.type,
|
||||
category: "uncategorized",
|
||||
isActive: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: null,
|
||||
}
|
||||
});
|
||||
}),
|
||||
];
|
||||
4
__tests__/mocks/server.ts
Normal file
4
__tests__/mocks/server.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { setupServer } from 'msw/node';
|
||||
import { handlers } from './handlers';
|
||||
|
||||
export const server = setupServer(...handlers);
|
||||
7
__tests__/setup.ts
Normal file
7
__tests__/setup.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { server } from './mocks/server';
|
||||
import { beforeAll, afterEach, afterAll } from 'vitest';
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterAll(() => server.close());
|
||||
14
package.json
14
package.json
@@ -5,7 +5,10 @@
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
"start": "next start",
|
||||
"test:api": "vitest run",
|
||||
"test:e2e": "playwright test",
|
||||
"test": "bun run test:api && bun run test:e2e"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "bun run prisma/seed.ts"
|
||||
@@ -50,6 +53,7 @@
|
||||
"add": "^2.0.6",
|
||||
"adm-zip": "^0.5.16",
|
||||
"animate.css": "^4.1.1",
|
||||
"async-mutex": "^0.5.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"bun": "^1.2.2",
|
||||
"chart.js": "^4.4.8",
|
||||
@@ -105,17 +109,23 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@vitest/ui": "^4.0.18",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"jsdom": "^28.0.0",
|
||||
"msw": "^2.12.9",
|
||||
"parcel": "^2.6.2",
|
||||
"postcss": "^8.5.1",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5",
|
||||
"vitest": "^4.0.18"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e7]:
|
||||
- button "Darmasaba Logo" [ref=e8] [cursor=pointer]:
|
||||
- img "Darmasaba Logo" [ref=e10]
|
||||
- button "PPID" [ref=e11] [cursor=pointer]:
|
||||
- generic [ref=e13]: PPID
|
||||
- button "Desa" [ref=e14] [cursor=pointer]:
|
||||
- generic [ref=e16]: Desa
|
||||
- button "Kesehatan" [ref=e17] [cursor=pointer]:
|
||||
- generic [ref=e19]: Kesehatan
|
||||
- button "Keamanan" [ref=e20] [cursor=pointer]:
|
||||
- generic [ref=e22]: Keamanan
|
||||
- button "Ekonomi" [ref=e23] [cursor=pointer]:
|
||||
- generic [ref=e25]: Ekonomi
|
||||
- button "Inovasi" [ref=e26] [cursor=pointer]:
|
||||
- generic [ref=e28]: Inovasi
|
||||
- button "Lingkungan" [ref=e29] [cursor=pointer]:
|
||||
- generic [ref=e31]: Lingkungan
|
||||
- button "Pendidikan" [ref=e32] [cursor=pointer]:
|
||||
- generic [ref=e34]: Pendidikan
|
||||
- button "Musik" [ref=e35] [cursor=pointer]:
|
||||
- generic [ref=e37]: Musik
|
||||
- button [ref=e38] [cursor=pointer]:
|
||||
- img [ref=e40]
|
||||
- generic [ref=e46]:
|
||||
- generic [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- generic [ref=e53]:
|
||||
- img "Logo Darmasaba" [ref=e55]
|
||||
- img "Logo Pudak" [ref=e57]
|
||||
- generic [ref=e63]:
|
||||
- generic [ref=e65]:
|
||||
- generic [ref=e66]:
|
||||
- img [ref=e67]
|
||||
- paragraph [ref=e71]: Jam Operasional
|
||||
- generic [ref=e72]:
|
||||
- generic [ref=e74]: Buka
|
||||
- paragraph [ref=e75]: 07:30 - 15:30
|
||||
- generic [ref=e77]:
|
||||
- generic [ref=e78]:
|
||||
- img [ref=e79]
|
||||
- paragraph [ref=e82]: Hari Ini
|
||||
- generic [ref=e83]:
|
||||
- paragraph [ref=e84]: Status Kantor
|
||||
- paragraph [ref=e85]: Sedang Beroperasi
|
||||
- paragraph [ref=e95]: Bagikan ide, kritik, atau saran Anda untuk mendukung pembangunan desa. Semua lebih mudah dengan fitur interaktif yang kami sediakan.
|
||||
- generic [ref=e102]:
|
||||
- generic [ref=e103]: Browser Anda tidak mendukung video.
|
||||
- generic [ref=e106]:
|
||||
- heading "Penghargaan Desa" [level=2] [ref=e107]
|
||||
- paragraph [ref=e110]: Sedang memuat data penghargaan...
|
||||
- button "Lihat semua penghargaan" [ref=e111] [cursor=pointer]:
|
||||
- generic [ref=e112]:
|
||||
- paragraph [ref=e114]: Lihat Semua Penghargaan
|
||||
- img [ref=e116]
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e121]:
|
||||
- heading "Layanan" [level=1] [ref=e122]
|
||||
- paragraph [ref=e123]: Layanan adalah fitur yang membantu warga desa mengakses berbagai kebutuhan administrasi, informasi, dan bantuan secara cepat, mudah, dan transparan. Dengan fitur ini, semua layanan desa ada dalam genggaman Anda!
|
||||
- link "Detail" [ref=e125] [cursor=pointer]:
|
||||
- /url: /darmasaba/desa/layanan
|
||||
- generic [ref=e127]: Detail
|
||||
- separator [ref=e129]
|
||||
- generic [ref=e130]:
|
||||
- generic [ref=e131]:
|
||||
- paragraph [ref=e132]: Potensi Desa
|
||||
- paragraph [ref=e133]: Jelajahi berbagai potensi dan peluang yang dimiliki desa. Fitur ini membantu warga maupun pemerintah desa dalam merencanakan dan mengembangkan program berbasis kekuatan lokal.
|
||||
- paragraph [ref=e136]: Sedang memuat potensi desa...
|
||||
- button "Lihat Semua Potensi" [ref=e139] [cursor=pointer]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]: Lihat Semua Potensi
|
||||
- img [ref=e143]
|
||||
- separator [ref=e146]
|
||||
- generic [ref=e147]:
|
||||
- generic [ref=e148]:
|
||||
- paragraph [ref=e150]: Desa Anti Korupsi
|
||||
- paragraph [ref=e151]: Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola secara terbuka dengan melibatkan warga dalam pengawasan anggaran, sehingga digunakan tepat sasaran dan sesuai kebutuhan masyarakat.
|
||||
- link "Selengkapnya" [ref=e153] [cursor=pointer]:
|
||||
- /url: /darmasaba/desa-anti-korupsi/detail
|
||||
- generic [ref=e155]: Selengkapnya
|
||||
- paragraph [ref=e158]: Memuat Data...
|
||||
- generic [ref=e166]:
|
||||
- heading "SDGs Desa" [level=1] [ref=e168]
|
||||
- paragraph [ref=e169]: SDGs Desa adalah upaya desa untuk menciptakan pembangunan yang maju, inklusif, dan berkelanjutan melalui 17 tujuan mulai dari pengentasan kemiskinan, pendidikan, kesehatan, hingga pelestarian lingkungan.
|
||||
- generic [ref=e170]:
|
||||
- generic [ref=e171]:
|
||||
- img [ref=e172]
|
||||
- paragraph [ref=e175]: Data SDGs Desa belum tersedia
|
||||
- link "Jelajahi Semua Tujuan SDGs Desa" [ref=e177] [cursor=pointer]:
|
||||
- /url: /darmasaba/sdgs-desa
|
||||
- paragraph [ref=e180]: Jelajahi Semua Tujuan SDGs Desa
|
||||
- generic [ref=e181]:
|
||||
- generic [ref=e183]:
|
||||
- heading "APBDes" [level=1] [ref=e184]
|
||||
- paragraph [ref=e185]: Transparansi APBDes Darmasaba adalah langkah nyata menuju tata kelola desa yang bersih, terbuka, dan bertanggung jawab.
|
||||
- link "Lihat Semua Data" [ref=e187] [cursor=pointer]:
|
||||
- /url: /darmasaba/apbdes
|
||||
- generic [ref=e189]: Lihat Semua Data
|
||||
- generic [ref=e191]:
|
||||
- paragraph [ref=e193]: Pilih Tahun APBDes
|
||||
- generic [ref=e194]:
|
||||
- textbox "Pilih Tahun APBDes" [ref=e195]:
|
||||
- /placeholder: Pilih tahun
|
||||
- generic:
|
||||
- img
|
||||
- paragraph [ref=e197]: Tidak ada data APBDes untuk tahun yang dipilih.
|
||||
- generic [ref=e202]:
|
||||
- heading "Prestasi Desa" [level=1] [ref=e203]
|
||||
- paragraph [ref=e204]: Kami bangga dengan pencapaian desa hingga saat ini. Semoga prestasi ini menjadi inspirasi untuk terus berkarya dan berinovasi demi kemajuan bersama.
|
||||
- link "Lihat Semua Prestasi" [ref=e205] [cursor=pointer]:
|
||||
- /url: /darmasaba/prestasi-desa
|
||||
- generic [ref=e207]: Lihat Semua Prestasi
|
||||
- button [ref=e211] [cursor=pointer]:
|
||||
- img [ref=e214]
|
||||
- button [ref=e219] [cursor=pointer]:
|
||||
- img [ref=e221]
|
||||
- generic [ref=e225]:
|
||||
- contentinfo [ref=e228]:
|
||||
- generic [ref=e230]:
|
||||
- generic [ref=e231]:
|
||||
- heading "Komitmen Layanan Kami" [level=2] [ref=e232]
|
||||
- generic [ref=e233]:
|
||||
- generic [ref=e234]:
|
||||
- paragraph [ref=e235]: "1. Transparansi:"
|
||||
- paragraph [ref=e236]: Pengelolaan dana desa dilakukan secara terbuka agar masyarakat dapat memahami dan memantau penggunaan anggaran.
|
||||
- generic [ref=e237]:
|
||||
- paragraph [ref=e238]: "2. Profesionalisme:"
|
||||
- paragraph [ref=e239]: Layanan desa diberikan secara cepat, adil, dan profesional demi kepuasan masyarakat.
|
||||
- generic [ref=e240]:
|
||||
- paragraph [ref=e241]: "3. Partisipasi:"
|
||||
- paragraph [ref=e242]: Masyarakat dilibatkan aktif dalam pengambilan keputusan demi pembangunan desa yang berhasil.
|
||||
- generic [ref=e243]:
|
||||
- paragraph [ref=e244]: "4. Inovasi:"
|
||||
- paragraph [ref=e245]: Kami terus berinovasi, termasuk melalui teknologi, agar layanan semakin mudah diakses.
|
||||
- generic [ref=e246]:
|
||||
- paragraph [ref=e247]: "5. Keadilan:"
|
||||
- paragraph [ref=e248]: Kebijakan dan program disusun untuk memberi manfaat yang merata bagi seluruh warga.
|
||||
- generic [ref=e249]:
|
||||
- paragraph [ref=e250]: "6. Pemberdayaan:"
|
||||
- paragraph [ref=e251]: Masyarakat didukung melalui pelatihan, pendampingan, dan pengembangan usaha lokal.
|
||||
- generic [ref=e252]:
|
||||
- paragraph [ref=e253]: "7. Ramah Lingkungan:"
|
||||
- paragraph [ref=e254]: Seluruh kegiatan pembangunan memperhatikan keberlanjutan demi menjaga alam dan kesehatan warga.
|
||||
- separator [ref=e255]
|
||||
- generic [ref=e256]:
|
||||
- heading "Visi Kami" [level=2] [ref=e257]
|
||||
- paragraph [ref=e258]: Dengan visi ini, kami berkomitmen menjadikan desa sebagai tempat yang aman, sejahtera, dan nyaman bagi seluruh warga.
|
||||
- paragraph [ref=e259]: Kami percaya kemajuan dimulai dari kerja sama antara pemerintah desa dan masyarakat, didukung tata kelola yang baik demi kepentingan bersama. Saran maupun keluhan dapat disampaikan melalui kontak di bawah ini.
|
||||
- generic [ref=e260]:
|
||||
- paragraph [ref=e261]: "\"Desa Kuat, Warga Sejahtera!\""
|
||||
- button "Logo Desa" [ref=e262] [cursor=pointer]:
|
||||
- generic [ref=e263]:
|
||||
- img "Logo Desa"
|
||||
- generic [ref=e265]:
|
||||
- generic [ref=e267]:
|
||||
- paragraph [ref=e268]: Tentang Darmasaba
|
||||
- paragraph [ref=e269]: Darmasaba adalah desa budaya yang kaya akan tradisi dan nilai-nilai warisan Bali.
|
||||
- generic [ref=e270]:
|
||||
- link [ref=e271] [cursor=pointer]:
|
||||
- /url: https://www.facebook.com/DarmasabaDesaku
|
||||
- img [ref=e273]
|
||||
- link [ref=e275] [cursor=pointer]:
|
||||
- /url: https://www.instagram.com/ddarmasaba/
|
||||
- img [ref=e277]
|
||||
- link [ref=e280] [cursor=pointer]:
|
||||
- /url: https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg
|
||||
- img [ref=e282]
|
||||
- link [ref=e285] [cursor=pointer]:
|
||||
- /url: https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc
|
||||
- img [ref=e287]
|
||||
- generic [ref=e290]:
|
||||
- paragraph [ref=e291]: Layanan Desa
|
||||
- link "Administrasi Kependudukan" [ref=e292] [cursor=pointer]:
|
||||
- /url: /darmasaba/desa/layanan/
|
||||
- link "Layanan Sosial" [ref=e293] [cursor=pointer]:
|
||||
- /url: /darmasaba/ekonomi/program-kemiskinan
|
||||
- link "Pengaduan Masyarakat" [ref=e294] [cursor=pointer]:
|
||||
- /url: /darmasaba/keamanan/laporan-publik
|
||||
- link "Informasi Publik" [ref=e295] [cursor=pointer]:
|
||||
- /url: /darmasaba/ppid/daftar-informasi-publik-desa-darmasaba
|
||||
- generic [ref=e297]:
|
||||
- paragraph [ref=e298]: Tautan Penting
|
||||
- link "Portal Badung" [ref=e299] [cursor=pointer]:
|
||||
- /url: /darmasaba/desa/berita/semua
|
||||
- link "E-Government" [ref=e300] [cursor=pointer]:
|
||||
- /url: /darmasaba/inovasi/desa-digital-smart-village
|
||||
- link "Transparansi" [ref=e301] [cursor=pointer]:
|
||||
- /url: /darmasaba/ppid/daftar-informasi-publik-desa-darmasaba
|
||||
- generic [ref=e303]:
|
||||
- paragraph [ref=e304]: Berlangganan Info
|
||||
- paragraph [ref=e305]: Dapatkan kabar terbaru tentang program dan kegiatan desa langsung ke email Anda.
|
||||
- generic [ref=e306]:
|
||||
- generic [ref=e308]:
|
||||
- textbox "Masukkan email Anda" [ref=e309]
|
||||
- img [ref=e311]
|
||||
- button "Daftar" [ref=e314] [cursor=pointer]:
|
||||
- generic [ref=e316]: Daftar
|
||||
- separator [ref=e317]
|
||||
- paragraph [ref=e318]: © 2025 Desa Darmasaba. Hak cipta dilindungi.
|
||||
- region "Notifications Alt+T"
|
||||
- button "Open Next.js Dev Tools" [ref=e324] [cursor=pointer]:
|
||||
- img [ref=e325]
|
||||
- alert [ref=e328]
|
||||
```
|
||||
85
playwright-report/index.html
Normal file
85
playwright-report/index.html
Normal file
File diff suppressed because one or more lines are too long
25
playwright.config.ts
Normal file
25
playwright.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './__tests__/e2e',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: 'html',
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
webServer: {
|
||||
command: 'bun run dev',
|
||||
url: 'http://localhost:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import maskotDesa from "../../../data/desa/profile/maskot_desa.json";
|
||||
import profilePerbekel from "../../../data/desa/profile/profil_perbekel.json";
|
||||
import profileDesaImage from "../../../data/desa/profile/profileDesaImage.json";
|
||||
import sejarahDesa from "../../../data/desa/profile/sejarah_desa.json";
|
||||
import visiMisiDesa from "../../../data/desa/profile/visi_misi_desa.json";
|
||||
|
||||
export async function seedProfileDesa() {
|
||||
// =========== SEJARAH DESA ===========
|
||||
@@ -26,6 +27,26 @@ export async function seedProfileDesa() {
|
||||
|
||||
console.log("sejarah desa success ...");
|
||||
|
||||
// =========== VISI MISI DESA ===========
|
||||
for (const l of visiMisiDesa) {
|
||||
await prisma.visiMisiDesa.upsert({
|
||||
where: {
|
||||
id: l.id,
|
||||
},
|
||||
update: {
|
||||
visi: l.visi,
|
||||
misi: l.misi,
|
||||
},
|
||||
create: {
|
||||
id: l.id,
|
||||
visi: l.visi,
|
||||
misi: l.misi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("visi misi desa success ...");
|
||||
|
||||
// =========== MASKOT DESA ===========
|
||||
for (const l of maskotDesa) {
|
||||
await prisma.maskotDesa.upsert({
|
||||
|
||||
25
prisma/_seeder_list/ekonomi/seed_demografi_pekerjaan.ts
Normal file
25
prisma/_seeder_list/ekonomi/seed_demografi_pekerjaan.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import demografiPekerjaan from "../../data/ekonomi/demografi-pekerjaan/demografi-pekerjaan.json";
|
||||
|
||||
export async function seedDemografiPekerjaan() {
|
||||
console.log("🔄 Seeding Demografi Pekerjaan...");
|
||||
for (const k of demografiPekerjaan) {
|
||||
await prisma.dataDemografiPekerjaan.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
pekerjaan: k.pekerjaan,
|
||||
lakiLaki: k.lakiLaki,
|
||||
perempuan: k.perempuan,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
pekerjaan: k.pekerjaan,
|
||||
lakiLaki: k.lakiLaki,
|
||||
perempuan: k.perempuan,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Demografi Pekerjaan seeded successfully");
|
||||
}
|
||||
23
prisma/_seeder_list/ekonomi/seed_jumlah_penduduk_miskin.ts
Normal file
23
prisma/_seeder_list/ekonomi/seed_jumlah_penduduk_miskin.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import jumlahPendudukMiskin from "../../data/ekonomi/jumlah-penduduk-miskin/jumlah-penduduk-miskin.json";
|
||||
|
||||
export async function seedJumlahPendudukMiskin() {
|
||||
console.log("🔄 Seeding Jumlah Penduduk Miskin...");
|
||||
for (const k of jumlahPendudukMiskin) {
|
||||
await prisma.grafikJumlahPendudukMiskin.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
year: k.year,
|
||||
totalPoorPopulation: k.totalPoorPopulation,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
year: k.year,
|
||||
totalPoorPopulation: k.totalPoorPopulation,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Jumlah Penduduk Miskin seeded successfully");
|
||||
}
|
||||
27
prisma/_seeder_list/ekonomi/seed_jumlah_pengangguran.ts
Normal file
27
prisma/_seeder_list/ekonomi/seed_jumlah_pengangguran.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import jumlahPengangguran from "../../data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
|
||||
|
||||
export async function seedJumlahPengangguran() {
|
||||
for (const d of jumlahPengangguran) {
|
||||
await prisma.detailDataPengangguran.upsert({
|
||||
where: {
|
||||
month_year: { month: d.month, year: d.year },
|
||||
},
|
||||
update: {
|
||||
totalUnemployment: d.totalUnemployment,
|
||||
educatedUnemployment: d.educatedUnemployment,
|
||||
uneducatedUnemployment: d.uneducatedUnemployment,
|
||||
percentageChange: d.percentageChange,
|
||||
},
|
||||
create: {
|
||||
month: d.month,
|
||||
year: d.year,
|
||||
totalUnemployment: d.totalUnemployment,
|
||||
educatedUnemployment: d.educatedUnemployment,
|
||||
uneducatedUnemployment: d.uneducatedUnemployment,
|
||||
percentageChange: d.percentageChange,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("📊 detailDataPengangguran success ...");
|
||||
}
|
||||
35
prisma/_seeder_list/ekonomi/seed_lowongan_kerja_lokal.ts
Normal file
35
prisma/_seeder_list/ekonomi/seed_lowongan_kerja_lokal.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import lowonganKerjaLokal from "../../data/ekonomi/lowongan-kerja-lokal/lowongan-kerja-lokal.json";
|
||||
|
||||
export async function seedLowonganKerjaLokal() {
|
||||
console.log("🔄 Seeding Lowongan Kerja Lokal...");
|
||||
for (const k of lowonganKerjaLokal) {
|
||||
await prisma.lowonganPekerjaan.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
posisi: k.posisi,
|
||||
namaPerusahaan: k.namaPerusahaan,
|
||||
lokasi: k.lokasi,
|
||||
tipePekerjaan: k.tipePekerjaan,
|
||||
gaji: k.gaji,
|
||||
deskripsi: k.deskripsi,
|
||||
kualifikasi: k.kualifikasi,
|
||||
notelp: k.notelp,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
posisi: k.posisi,
|
||||
namaPerusahaan: k.namaPerusahaan,
|
||||
lokasi: k.lokasi,
|
||||
tipePekerjaan: k.tipePekerjaan,
|
||||
gaji: k.gaji,
|
||||
deskripsi: k.deskripsi,
|
||||
kualifikasi: k.kualifikasi,
|
||||
notelp: k.notelp,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Lowongan Kerja Lokal seeded successfully");
|
||||
}
|
||||
91
prisma/_seeder_list/ekonomi/seed_pasar_desa.ts
Normal file
91
prisma/_seeder_list/ekonomi/seed_pasar_desa.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import kategoriProduk from "../../data/ekonomi/pasar-desa/kategori-produk.json";
|
||||
import pasarDesa from "../../data/ekonomi/pasar-desa/pasar-desa.json";
|
||||
import kategoriToPasar from "../../data/ekonomi/pasar-desa/kategori-to-pasar.json";
|
||||
|
||||
export async function seedPasarDesa() {
|
||||
console.log("🔄 Seeding Kategori Produk...");
|
||||
for (const k of kategoriProduk) {
|
||||
await prisma.kategoriProduk.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
nama: k.nama,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
nama: k.nama,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kategori Produk seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Pasar Desa...");
|
||||
|
||||
for (const p of pasarDesa) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for pasar desa "${p.nama}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.pasarDesa.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
nama: p.nama,
|
||||
deskripsi: p.deskripsi,
|
||||
harga: p.harga,
|
||||
rating: p.rating,
|
||||
alamatUsaha: p.alamatUsaha,
|
||||
kontak: p.kontak,
|
||||
imageId,
|
||||
kategoriProdukId: p.kategoriProdukId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
nama: p.nama,
|
||||
deskripsi: p.deskripsi,
|
||||
harga: p.harga,
|
||||
rating: p.rating,
|
||||
alamatUsaha: p.alamatUsaha,
|
||||
kontak: p.kontak,
|
||||
imageId,
|
||||
kategoriProdukId: p.kategoriProdukId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Pasar desa seeded: ${p.nama}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Pasar desa seed selesai");
|
||||
|
||||
console.log("🔄 Seeding Kategori To Pasar...");
|
||||
for (const p of kategoriToPasar) {
|
||||
await prisma.kategoriToPasar.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
kategoriId: p.kategoriId,
|
||||
pasarDesaId: p.pasarDesaId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
kategoriId: p.kategoriId,
|
||||
pasarDesaId: p.pasarDesaId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
81
prisma/_seeder_list/ekonomi/seed_pendapatan_asli.ts
Normal file
81
prisma/_seeder_list/ekonomi/seed_pendapatan_asli.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import apbdes from "../../data/ekonomi/pendapatan-asli-desa/apbDesa.json";
|
||||
import pendapatan from "../../data/ekonomi/pendapatan-asli-desa/pendapatanDesa.json";
|
||||
import belanja from "../../data/ekonomi/pendapatan-asli-desa/belanjaDesa.json";
|
||||
import pembiayaan from "../../data/ekonomi/pendapatan-asli-desa/pembiayaanDesa.json";
|
||||
|
||||
export async function seedPendapatanAsli() {
|
||||
console.log("🔄 Seeding Pendapatan Asli...");
|
||||
for (const d of apbdes) {
|
||||
await prisma.apbDesa.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
tahun: d.tahun,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
tahun: d.tahun,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Pendapatan Asli seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Pendapatan...");
|
||||
for (const d of pendapatan) {
|
||||
await prisma.pendapatan.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Pendapatan seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Belanja...");
|
||||
for (const d of belanja) {
|
||||
await prisma.belanja.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Belanja seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Pembiayaan...");
|
||||
for (const d of pembiayaan) {
|
||||
await prisma.pembiayaan.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
value: d.nilai
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Pembiayaan seeded successfully");
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import grafikMenganggurBerdasarkanUsia from "../../data/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran-berdasarkan-usia.json";
|
||||
import grafikMenganggurBerdasarkanPendidikan from "../../data/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran-berdasarkan-pendidikan.json";
|
||||
|
||||
export async function seedPendudukUsiaKerjaYangMenganggur() {
|
||||
for (const p of grafikMenganggurBerdasarkanUsia) {
|
||||
await prisma.grafikMenganggurBerdasarkanUsia.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
usia18_25: p.usia18_25,
|
||||
usia26_35: p.usia26_35,
|
||||
usia36_45: p.usia36_45,
|
||||
usia46_keatas: p.usia46_keatas,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
usia18_25: p.usia18_25,
|
||||
usia26_35: p.usia26_35,
|
||||
usia36_45: p.usia36_45,
|
||||
usia46_keatas: p.usia46_keatas,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("📊 grafikMenganggurBerdasarkanUsia success ...");
|
||||
for (const p of grafikMenganggurBerdasarkanPendidikan) {
|
||||
await prisma.grafikMenganggurBerdasarkanPendidikan.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
SD: p.SD,
|
||||
SMP: p.SMP,
|
||||
SMA: p.SMA,
|
||||
D3: p.D3,
|
||||
S1: p.S1,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
SD: p.SD,
|
||||
SMP: p.SMP,
|
||||
SMA: p.SMA,
|
||||
D3: p.D3,
|
||||
S1: p.S1,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("📊 grafikMenganggurBerdasarkanUsia success ...");
|
||||
}
|
||||
50
prisma/_seeder_list/ekonomi/seed_program_kemiskinan.ts
Normal file
50
prisma/_seeder_list/ekonomi/seed_program_kemiskinan.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import programKemiskinan from "../../data/ekonomi/program-kemiskinan/program-kemiskinan.json";
|
||||
import statistikKemiskinan from "../../data/ekonomi/program-kemiskinan/statistik-kemiskinan.json";
|
||||
|
||||
export async function seedProgramKemiskinan() {
|
||||
for (const s of statistikKemiskinan) {
|
||||
await prisma.statistikKemiskinan.upsert({
|
||||
where: { tahun: s.tahun }, // ✅ FIX
|
||||
update: {
|
||||
jumlah: s.jumlah,
|
||||
},
|
||||
create: {
|
||||
id: s.id, // id boleh tetap
|
||||
tahun: s.tahun,
|
||||
jumlah: s.jumlah,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("📊 Statistik Kemiskinan seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Program Kemiskinan...");
|
||||
for (const k of programKemiskinan) {
|
||||
await prisma.programKemiskinan.upsert({
|
||||
where: { id: k.id },
|
||||
update: {
|
||||
nama: k.nama,
|
||||
deskripsi: k.deskripsi,
|
||||
icon: k.icon,
|
||||
statistik: {
|
||||
connect: {
|
||||
tahun: k.tahun, // 👈 BUKAN ID
|
||||
},
|
||||
},
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
nama: k.nama,
|
||||
deskripsi: k.deskripsi,
|
||||
icon: k.icon,
|
||||
statistik: {
|
||||
connect: {
|
||||
tahun: k.tahun,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Program Kemiskinan seeded successfully");
|
||||
}
|
||||
25
prisma/_seeder_list/ekonomi/seed_sektor_unggulan_desa.ts
Normal file
25
prisma/_seeder_list/ekonomi/seed_sektor_unggulan_desa.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import sektorUnggulanDesa from "../../data/ekonomi/sektor-unggulan/sektor-unggulan.json";
|
||||
|
||||
export async function seedSektorUnggulanDesa() {
|
||||
console.log("🔄 Seeding Sektor Unggulan Desa...");
|
||||
for (const k of sektorUnggulanDesa) {
|
||||
await prisma.sektorUnggulanDesa.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
name: k.name,
|
||||
description: k.description,
|
||||
value: k.value,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
name: k.name,
|
||||
description: k.description,
|
||||
value: k.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Sektor Unggulan Desa seeded successfully");
|
||||
}
|
||||
58
prisma/_seeder_list/ekonomi/seed_struktur_bumdes.ts
Normal file
58
prisma/_seeder_list/ekonomi/seed_struktur_bumdes.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import posisiOrganisasiBumDes from "../../data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json";
|
||||
import pegawai from "../../data/ekonomi/struktur-organisasi/pegawai-bumdes.json";
|
||||
|
||||
export async function seedStrukturBumdes() {
|
||||
const flattenedPosisi = posisiOrganisasiBumDes.flat();
|
||||
|
||||
// ✅ Urutkan berdasarkan hierarki
|
||||
const sortedPosisi = flattenedPosisi.sort((a, b) => a.hierarki - b.hierarki);
|
||||
|
||||
for (const p of sortedPosisi) {
|
||||
console.log(`Seeding: ${p.nama} (id: ${p.id}, parent: ${p.parentId})`);
|
||||
if (p.parentId) {
|
||||
const parentExists = flattenedPosisi.some((pos) => pos.id === p.parentId);
|
||||
if (!parentExists) {
|
||||
console.warn(
|
||||
`⚠️ Parent tidak ditemukan: ${p.parentId} untuk ${p.nama}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
await prisma.posisiOrganisasiBumDes.upsert({
|
||||
where: { id: p.id },
|
||||
update: p,
|
||||
create: p,
|
||||
});
|
||||
}
|
||||
console.log("posisi organisasi berhasil");
|
||||
for (const p of pegawai) {
|
||||
await prisma.pegawaiBumDes.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
namaLengkap: p.namaLengkap,
|
||||
gelarAkademik: p.gelarAkademik,
|
||||
tanggalMasuk: new Date(p.tanggalMasuk),
|
||||
email: p.email,
|
||||
telepon: p.telepon,
|
||||
alamat: p.alamat,
|
||||
posisiId: p.posisiId,
|
||||
isActive: p.isActive,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
namaLengkap: p.namaLengkap,
|
||||
gelarAkademik: p.gelarAkademik,
|
||||
tanggalMasuk: new Date(p.tanggalMasuk),
|
||||
email: p.email,
|
||||
telepon: p.telepon,
|
||||
alamat: p.alamat,
|
||||
posisiId: p.posisiId,
|
||||
isActive: p.isActive,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("pegawai success ...");
|
||||
}
|
||||
31
prisma/_seeder_list/inovasi/seed_ajukan.ts
Normal file
31
prisma/_seeder_list/inovasi/seed_ajukan.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import ajukanIde from "../../data/inovasi/ajukan-ide/ajukan-ide.json";
|
||||
|
||||
export async function seedAjukan() {
|
||||
console.log("🔄 Seeding Ajukan Ide Inovatif...");
|
||||
for (const d of ajukanIde) {
|
||||
await prisma.ajukanIdeInovatif.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
alamat: d.alamat,
|
||||
namaIde: d.namaIde,
|
||||
deskripsi: d.deskripsi,
|
||||
masalah: d.masalah,
|
||||
benefit: d.benefit,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
alamat: d.alamat,
|
||||
namaIde: d.namaIde,
|
||||
deskripsi: d.deskripsi,
|
||||
masalah: d.masalah,
|
||||
benefit: d.benefit,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Ajukan Ide Inovatif seeded successfully");
|
||||
}
|
||||
42
prisma/_seeder_list/inovasi/seed_desa_digital.ts
Normal file
42
prisma/_seeder_list/inovasi/seed_desa_digital.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import desaDigital from "../../data/inovasi/desa-digital/desa-digital.json";
|
||||
|
||||
export async function seedDesaDigital() {
|
||||
console.log("🔄 Seeding Desa Digital...");
|
||||
for (const d of desaDigital) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (d.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: d.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for desa digital "${d.name}": ${d.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.desaDigital.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
deskripsi: d.deskripsi,
|
||||
imageId: imageId,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
deskripsi: d.deskripsi,
|
||||
imageId: imageId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Desa Digital seeded successfully");
|
||||
}
|
||||
42
prisma/_seeder_list/inovasi/seed_info_teknologi.ts
Normal file
42
prisma/_seeder_list/inovasi/seed_info_teknologi.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import infoTeknologi from "../../data/inovasi/info-teknologi/info-teknologi.json";
|
||||
|
||||
export async function seedInfoTeknologi() {
|
||||
console.log("🔄 Seeding Info Teknologi...");
|
||||
for (const p of infoTeknologi) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for berita "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.infoTekno.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId: imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId: imageId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Info Teknologi seeded successfully");
|
||||
}
|
||||
66
prisma/_seeder_list/inovasi/seed_kolaborasi_inovasi.ts
Normal file
66
prisma/_seeder_list/inovasi/seed_kolaborasi_inovasi.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import kolaborasiInovasi from "../../data/inovasi/kolaborasi-inovasi/kolaborasi-inovasi.json";
|
||||
import mitraKolaborasi from "../../data/inovasi/kolaborasi-inovasi/mitra-kolaborasi.json";
|
||||
|
||||
export async function seedKolaborasiInovasi() {
|
||||
console.log("🔄 Seeding Kolaborasi Inovasi...");
|
||||
for (const p of kolaborasiInovasi) {
|
||||
await prisma.kolaborasiInovasi.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
tahun: p.tahun,
|
||||
slug: p.slug,
|
||||
deskripsi: p.deskripsi,
|
||||
kolaborator: p.kolaborator,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
tahun: p.tahun,
|
||||
slug: p.slug,
|
||||
deskripsi: p.deskripsi,
|
||||
kolaborator: p.kolaborator,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kolaborasi Inovasi seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Mitra Kolaborasi...");
|
||||
for (const p of mitraKolaborasi) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for mitra kolaborasi "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.mitraKolaborasi.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
imageId: imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
imageId: imageId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Mitra Kolaborasi seeded successfully");
|
||||
}
|
||||
113
prisma/_seeder_list/inovasi/seed_layanan_online_desa.ts
Normal file
113
prisma/_seeder_list/inovasi/seed_layanan_online_desa.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import jenisLayanan from "../../data/inovasi/layanan-online-desa/jenis-layanan.json";
|
||||
import administrasiOnline from "../../data/inovasi/layanan-online-desa/administrasi-online.json";
|
||||
import jenisPengaduan from "../../data/inovasi/layanan-online-desa/jenis-pengaduan.json";
|
||||
import pengaduanMasyarakat from "../../data/inovasi/layanan-online-desa/pengaduan-masyarakat.json";
|
||||
|
||||
export async function seedLayananOnlineDesa() {
|
||||
console.log("🔄 Seeding Jenis Layanan...");
|
||||
for (const j of jenisLayanan) {
|
||||
await prisma.jenisLayanan.upsert({
|
||||
where: {
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
nama: j.nama,
|
||||
deskripsi: j.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: j.id,
|
||||
nama: j.nama,
|
||||
deskripsi: j.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Jenis Layanan seeded successfully");
|
||||
console.log("🔄 Seeding Administrasi Online...");
|
||||
for (const d of administrasiOnline) {
|
||||
await prisma.administrasiOnline.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
alamat: d.alamat,
|
||||
nomorTelepon: d.nomorTelepon,
|
||||
jenisLayananId: d.jenisLayananId,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
alamat: d.alamat,
|
||||
nomorTelepon: d.nomorTelepon,
|
||||
jenisLayananId: d.jenisLayananId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Administrasi Online seeded successfully");
|
||||
console.log("🔄 Seeding Jenis Pengaduan Masyarakat...");
|
||||
for (const d of jenisPengaduan) {
|
||||
await prisma.jenisPengaduan.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
nama: d.nama,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
nama: d.nama,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Jenis Pengaduan Masyarakat seeded successfully");
|
||||
console.log("🔄 Seeding Pengaduan Masyarakat...");
|
||||
for (const d of pengaduanMasyarakat) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (d.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: d.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for pengaduan masyarakat "${d.name}": ${d.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.pengaduanMasyarakat.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
name: d.name,
|
||||
email: d.email,
|
||||
nik: d.nik,
|
||||
nomorTelepon: d.nomorTelepon,
|
||||
judulPengaduan: d.judulPengaduan,
|
||||
lokasiKejadian: d.lokasiKejadian,
|
||||
imageId: imageId,
|
||||
deskripsiPengaduan: d.deskripsiPengaduan,
|
||||
jenisPengaduanId: d.jenisPengaduanId,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
email: d.email,
|
||||
nik: d.nik,
|
||||
nomorTelepon: d.nomorTelepon,
|
||||
judulPengaduan: d.judulPengaduan,
|
||||
lokasiKejadian: d.lokasiKejadian,
|
||||
imageId: imageId,
|
||||
deskripsiPengaduan: d.deskripsiPengaduan,
|
||||
jenisPengaduanId: d.jenisPengaduanId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Pengaduan Masyarakat seeded successfully");
|
||||
}
|
||||
27
prisma/_seeder_list/inovasi/seed_program_kreatif_desa.ts
Normal file
27
prisma/_seeder_list/inovasi/seed_program_kreatif_desa.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import programKreatif from "../../data/inovasi/program-kreatif-desa/program-kreatif-desa.json";
|
||||
|
||||
export async function seedProgramKreatifDesa() {
|
||||
console.log("🔄 Seeding Program Kreatif...");
|
||||
for (const p of programKreatif) {
|
||||
await prisma.programKreatif.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
slug: p.slug,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
slug: p.slug,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Program Kreatif seeded successfully");
|
||||
}
|
||||
44
prisma/_seeder_list/keamanan/seed_keamanan_lingkungan.ts
Normal file
44
prisma/_seeder_list/keamanan/seed_keamanan_lingkungan.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import keamananLingkunganJson from "../../data/keamanan/keamanan-lingkungan/keamanan-lingkungan.json";
|
||||
|
||||
export async function seedKeamananLingkungan() {
|
||||
console.log("🔄 Seeding Keamanan Lingkungan...");
|
||||
|
||||
for (const p of keamananLingkunganJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for keamanan lingkungan "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.keamananLingkungan.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Keamanan lingkungan seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Keamanan lingkungan seed selesai");
|
||||
}
|
||||
87
prisma/_seeder_list/keamanan/seed_kontak_darurat.ts
Normal file
87
prisma/_seeder_list/keamanan/seed_kontak_darurat.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import kontakDaruratKeamanan from "../../data/keamanan/kontak-darurat-keamanan/kontak-darurat-keamanan.json";
|
||||
import kontakItem from "../../data/keamanan/kontak-darurat-keamanan/kontakItem.json";
|
||||
import kontakDaruratToItem from "../../data/keamanan/kontak-darurat-keamanan/kontakDaruratToItem.json";
|
||||
|
||||
export async function seedKontakDaruratKeamanan() {
|
||||
console.log("🔄 Seeding Kontak Item...");
|
||||
for (const e of kontakItem) {
|
||||
await prisma.kontakItem.upsert({
|
||||
where: {
|
||||
id: e.id,
|
||||
},
|
||||
update: {
|
||||
nama: e.nama,
|
||||
icon: e.icon,
|
||||
nomorTelepon: e.nomorTelepon,
|
||||
},
|
||||
create: {
|
||||
id: e.id, // ✅ WAJIB
|
||||
nama: e.nama,
|
||||
icon: e.icon,
|
||||
nomorTelepon: e.nomorTelepon,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kontak Item seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Kontak Darurat Keamanan...");
|
||||
for (const d of kontakDaruratKeamanan) {
|
||||
await prisma.kontakDaruratKeamanan.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
nama: d.nama,
|
||||
icon: d.icon,
|
||||
kategoriId: d.kategoriId,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
nama: d.nama,
|
||||
icon: d.icon,
|
||||
kategoriId: d.kategoriId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("✅ Kontak Darurat Keamanan seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Kontak Darurat To Item...");
|
||||
for (const f of kontakDaruratToItem) {
|
||||
// ✅ Validasi foreign keys
|
||||
const kontakDaruratExists = await prisma.kontakDaruratKeamanan.findUnique({
|
||||
where: { id: f.kontakDaruratId },
|
||||
});
|
||||
|
||||
const kontakItemExists = await prisma.kontakItem.findUnique({
|
||||
where: { id: f.kontakItemId },
|
||||
});
|
||||
|
||||
if (!kontakDaruratExists) {
|
||||
console.warn(
|
||||
`⚠️ KontakDarurat ${f.kontakDaruratId} not found, skipping...`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!kontakItemExists) {
|
||||
console.warn(`⚠️ KontakItem ${f.kontakItemId} not found, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await prisma.kontakDaruratToItem.upsert({
|
||||
where: { id: f.id },
|
||||
update: {
|
||||
kontakDaruratId: f.kontakDaruratId,
|
||||
kontakItemId: f.kontakItemId,
|
||||
},
|
||||
create: {
|
||||
id: f.id,
|
||||
kontakDaruratId: f.kontakDaruratId,
|
||||
kontakItemId: f.kontakItemId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kontak Darurat To Item seeded successfully");
|
||||
}
|
||||
49
prisma/_seeder_list/keamanan/seed_laporan_publik.ts
Normal file
49
prisma/_seeder_list/keamanan/seed_laporan_publik.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import laporanPublik from "../../data/keamanan/laporan-publik/laporan-publik.json";
|
||||
import penangananLaporan from "../../data/keamanan/laporan-publik/penanganan-laporan.json";
|
||||
|
||||
export async function seedLaporanPublik() {
|
||||
console.log("🔄 Seeding Laporan Publik...");
|
||||
for (const l of laporanPublik) {
|
||||
await prisma.laporanPublik.upsert({
|
||||
where: {
|
||||
id: l.id,
|
||||
},
|
||||
update: {
|
||||
judul: l.judul,
|
||||
lokasi: l.lokasi,
|
||||
tanggalWaktu: l.tanggalWaktu,
|
||||
kronologi: l.kronologi,
|
||||
},
|
||||
create: {
|
||||
id: l.id,
|
||||
judul: l.judul,
|
||||
lokasi: l.lokasi,
|
||||
tanggalWaktu: l.tanggalWaktu,
|
||||
kronologi: l.kronologi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("laporan publik success ...");
|
||||
|
||||
console.log("🔄 Seeding Penanganan Laporan...");
|
||||
for (const l of penangananLaporan) {
|
||||
await prisma.penangananLaporanPublik.upsert({
|
||||
where: {
|
||||
id: l.id,
|
||||
},
|
||||
update: {
|
||||
deskripsi: l.deskripsi,
|
||||
laporanId: l.laporanId,
|
||||
},
|
||||
create: {
|
||||
id: l.id,
|
||||
deskripsi: l.deskripsi,
|
||||
laporanId: l.laporanId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("penanganan laporan success ...");
|
||||
}
|
||||
28
prisma/_seeder_list/keamanan/seed_pencegahan_kriminalitas.ts
Normal file
28
prisma/_seeder_list/keamanan/seed_pencegahan_kriminalitas.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import pencegahanKriminalitas from "../../data/keamanan/pencegahan-kriminalitas/pencegahan-kriminalitas.json";
|
||||
|
||||
export async function seedPencegahanKriminalitas() {
|
||||
console.log("🔄 Seeding Pencegahan Kriminalitas...");
|
||||
for (const d of pencegahanKriminalitas) {
|
||||
await prisma.pencegahanKriminalitas.upsert({
|
||||
where: {
|
||||
id: d.id,
|
||||
},
|
||||
update: {
|
||||
judul: d.judul,
|
||||
deskripsi: d.deskripsi,
|
||||
deskripsiSingkat: d.deskripsiSingkat,
|
||||
linkVideo: d.linkVideo,
|
||||
},
|
||||
create: {
|
||||
id: d.id,
|
||||
judul: d.judul,
|
||||
deskripsi: d.deskripsi,
|
||||
deskripsiSingkat: d.deskripsiSingkat,
|
||||
linkVideo: d.linkVideo,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("✅ Pencegahan Kriminalitas seeded successfully");
|
||||
}
|
||||
80
prisma/_seeder_list/keamanan/seed_polsek_terdekat.ts
Normal file
80
prisma/_seeder_list/keamanan/seed_polsek_terdekat.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import layananPolsek from "../../data/keamanan/polsek-terdekat/layanan-polsek.json";
|
||||
import polsekTerdekat from "../../data/keamanan/polsek-terdekat/polsek-terdekat.json";
|
||||
import layananToPolsek from "../../data/keamanan/polsek-terdekat/layanan-to-polsek.json";
|
||||
|
||||
export async function seedPolsekTerdekat() {
|
||||
console.log("🔄 Seeding Layanan Polsek...");
|
||||
for (const k of layananPolsek) {
|
||||
await prisma.layananPolsek.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
nama: k.nama,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
nama: k.nama,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("layanan polsek success ...");
|
||||
|
||||
console.log("🔄 Seeding Polsek Terdekat...");
|
||||
for (const k of polsekTerdekat) {
|
||||
await prisma.polsekTerdekat.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
nama: k.nama,
|
||||
jarakKeDesa: k.jarakKeDesa,
|
||||
alamat: k.alamat,
|
||||
nomorTelepon: k.nomorTelepon,
|
||||
jamOperasional: k.jamOperasional,
|
||||
embedMapUrl: k.embedMapUrl,
|
||||
namaTempatMaps: k.namaTempatMaps,
|
||||
alamatMaps: k.alamatMaps,
|
||||
linkPetunjukArah: k.linkPetunjukArah,
|
||||
layananPolsekId: k.layananPolsekId,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
nama: k.nama,
|
||||
jarakKeDesa: k.jarakKeDesa,
|
||||
alamat: k.alamat,
|
||||
nomorTelepon: k.nomorTelepon,
|
||||
jamOperasional: k.jamOperasional,
|
||||
embedMapUrl: k.embedMapUrl,
|
||||
namaTempatMaps: k.namaTempatMaps,
|
||||
alamatMaps: k.alamatMaps,
|
||||
linkPetunjukArah: k.linkPetunjukArah,
|
||||
layananPolsekId: k.layananPolsekId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("polsek terdekat success ...");
|
||||
|
||||
console.log("🔄 Seeding Layanan To Polsek...");
|
||||
for (const k of layananToPolsek) {
|
||||
await prisma.layananToPolsek.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
layananId: k.layananId,
|
||||
polsekTerdekatId: k.polsekTerdekatId,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
layananId: k.layananId,
|
||||
polsekTerdekatId: k.polsekTerdekatId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("layanan to polsek success ...");
|
||||
}
|
||||
44
prisma/_seeder_list/keamanan/seed_tips_keamanan.ts
Normal file
44
prisma/_seeder_list/keamanan/seed_tips_keamanan.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import tipsKeamananJson from "../../data/keamanan/tips-keamanan/tips-keamanan.json";
|
||||
|
||||
export async function seedTipsKeamanan() {
|
||||
console.log("🔄 Seeding Tips Keamanan...");
|
||||
|
||||
for (const p of tipsKeamananJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for tips keamanan "${p.judul}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.menuTipsKeamanan.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
judul: p.judul,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
judul: p.judul,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Tips Keamanan seeded: ${p.judul}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Tips Keamanan seed selesai");
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import infoWabahPenyakitJson from "../../../data/kesehatan/infowabahpenyakit/infowabahpenyakit.json";
|
||||
|
||||
export async function seedInfoWabahPenyakit() {
|
||||
console.log("🔄 Seeding Info Wabah Penyakit...");
|
||||
|
||||
for (const p of infoWabahPenyakitJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for info wabah penyakit "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.infoWabahPenyakit.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsiSingkat: p.deskripsiSingkat,
|
||||
deskripsiLengkap: p.deskripsiLengkap,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsiSingkat: p.deskripsiSingkat,
|
||||
deskripsiLengkap: p.deskripsiLengkap,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Info wabah penyakit seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Info wabah penyakit seed selesai");
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import kontakDaruratJson from "../../../data/kesehatan/kontak-darurat/kontak-darurat.json";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function seedKontakDarurat() {
|
||||
console.log("🔄 Seeding Kontak Darurat...");
|
||||
|
||||
for (const p of kontakDaruratJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for kontak darurat "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.kontakDarurat.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
whatsapp: p.whatsapp,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
whatsapp: p.whatsapp,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Kontak darurat seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Kontak darurat seed selesai");
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import penangananDaruratJson from "../../../data/kesehatan/penanganan-darurat/penganan-darurat.json";
|
||||
|
||||
export async function seedPenangananDarurat() {
|
||||
console.log("🔄 Seeding Penanganan Darurat...");
|
||||
|
||||
for (const p of penangananDaruratJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for penanganan darurat "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.penangananDarurat.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Penanganan darurat seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Penanganan darurat seed selesai");
|
||||
}
|
||||
48
prisma/_seeder_list/kesehatan/posyandu/seed_posyandu.ts
Normal file
48
prisma/_seeder_list/kesehatan/posyandu/seed_posyandu.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import posyanduJson from "../../../data/kesehatan/posyandu/posyandu.json";
|
||||
|
||||
export async function seedPosyandu() {
|
||||
console.log("🔄 Seeding Posyandu...");
|
||||
|
||||
for (const p of posyanduJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for posyandu "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.posyandu.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
nomor: p.nomor,
|
||||
deskripsi: p.deskripsi,
|
||||
jadwalPelayanan: p.jadwalPelayanan,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
nomor: p.nomor,
|
||||
deskripsi: p.deskripsi,
|
||||
jadwalPelayanan: p.jadwalPelayanan,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Posyandu seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Posyandu seed selesai");
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import programKesehatanJson from "../../../data/kesehatan/program-kesehatan/program-kesehatan.json";
|
||||
|
||||
export async function seedProgramKesehatan() {
|
||||
for (const p of programKesehatanJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for program kesehatan "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.programKesehatan.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsiSingkat: p.deskripsiSingkat,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsiSingkat: p.deskripsiSingkat,
|
||||
deskripsi: p.deskripsi,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Program kesehatan seeded: ${p.name}`);
|
||||
}
|
||||
}
|
||||
95
prisma/_seeder_list/kesehatan/puskesmas/seed_puskesmas.ts
Normal file
95
prisma/_seeder_list/kesehatan/puskesmas/seed_puskesmas.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import puskesmasJson from "../../../data/kesehatan/puskesmas/puskesmas.json";
|
||||
import kontakPuskesmasJson from "../../../data/kesehatan/puskesmas/kontak-puskesmas/kontak.json";
|
||||
import jamPuskesmasJson from "../../../data/kesehatan/puskesmas/jam-puskesmas/jam.json";
|
||||
|
||||
export async function seedPuskesmas() {
|
||||
console.log("🔄 Seeding Kontak Puskesmas...");
|
||||
for (const k of kontakPuskesmasJson) {
|
||||
await prisma.kontakPuskesmas.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
kontakPuskesmas: k.kontakPuskesmas,
|
||||
email: k.email,
|
||||
facebook: k.facebook,
|
||||
kontakUGD: k.kontakUGD,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
kontakPuskesmas: k.kontakPuskesmas,
|
||||
email: k.email,
|
||||
facebook: k.facebook,
|
||||
kontakUGD: k.kontakUGD,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("kontak puskesmas success ...");
|
||||
|
||||
console.log("🔄 Seeding Jam Puskesmas...");
|
||||
for (const k of jamPuskesmasJson) {
|
||||
await prisma.jamOperasional.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
workDays: k.workDays,
|
||||
weekDays: k.weekDays,
|
||||
holiday: k.holiday,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
workDays: k.workDays,
|
||||
weekDays: k.weekDays,
|
||||
holiday: k.holiday,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("jam puskesmas success ...");
|
||||
|
||||
console.log("🔄 Seeding Puskesmas...");
|
||||
|
||||
for (const p of puskesmasJson) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (p.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: p.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for puskesmas "${p.name}": ${p.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.puskesmas.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
alamat: p.alamat,
|
||||
jamId: p.jamId,
|
||||
kontakId: p.kontakId,
|
||||
imageId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
alamat: p.alamat,
|
||||
jamId: p.jamId,
|
||||
kontakId: p.kontakId,
|
||||
imageId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Puskesmas seeded: ${p.name}`);
|
||||
}
|
||||
|
||||
console.log("🎉 Puskesmas seed selesai");
|
||||
}
|
||||
|
||||
71
prisma/_seeder_list/lingkungan/seed_data_gotong_royong.ts
Normal file
71
prisma/_seeder_list/lingkungan/seed_data_gotong_royong.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import kategoriGotongRoyong from "../../data/lingkungan/gotong-royong/kategori-gotong-royong.json";
|
||||
import gotongRoyong from "../../data/lingkungan/gotong-royong/gotong-royong.json";
|
||||
|
||||
export async function seedDataGotongRoyong() {
|
||||
console.log("🔄 Seeding Kategori Gotong Royong...");
|
||||
|
||||
for (const k of kategoriGotongRoyong) {
|
||||
await prisma.kategoriKegiatan.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
nama: k.nama,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
nama: k.nama,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kategori Gotong Royong seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Gotong Royong...");
|
||||
for (const k of gotongRoyong) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (k.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: k.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for gotong royong "${k.judul}": ${k.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.kegiatanDesa.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
judul: k.judul,
|
||||
deskripsiSingkat: k.deskripsiSingkat,
|
||||
deskripsiLengkap: k.deskripsiLengkap,
|
||||
tanggal: k.tanggal,
|
||||
lokasi: k.lokasi,
|
||||
partisipan: k.partisipan,
|
||||
imageId: imageId,
|
||||
kategoriKegiatanId: k.kategoriKegiatanId,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
judul: k.judul,
|
||||
deskripsiSingkat: k.deskripsiSingkat,
|
||||
deskripsiLengkap: k.deskripsiLengkap,
|
||||
tanggal: k.tanggal,
|
||||
lokasi: k.lokasi,
|
||||
partisipan: k.partisipan,
|
||||
imageId: imageId,
|
||||
kategoriKegiatanId: k.kategoriKegiatanId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Gotong Royong seeded successfully");
|
||||
}
|
||||
27
prisma/_seeder_list/lingkungan/seed_data_lingkungan_desa.ts
Normal file
27
prisma/_seeder_list/lingkungan/seed_data_lingkungan_desa.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import dataLingkunganDesa from "../../data/lingkungan/data-lingkungan-desa/data-lingkungan-desa.json";
|
||||
|
||||
export async function seedDataLingkunganDesa() {
|
||||
console.log("🔄 Seeding Data Lingkungan Desa...");
|
||||
for (const p of dataLingkunganDesa) {
|
||||
await prisma.dataLingkunganDesa.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
jumlah: p.jumlah,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
jumlah: p.jumlah,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Data Lingkungan Desa seeded successfully");
|
||||
}
|
||||
63
prisma/_seeder_list/lingkungan/seed_edukasi_lingkungan.ts
Normal file
63
prisma/_seeder_list/lingkungan/seed_edukasi_lingkungan.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import tujuanEdukasiLingkungan from "../../data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
|
||||
import materiEdukasiLingkungan from "../../data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json";
|
||||
import contohEdukasiLingkungan from "../../data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json";
|
||||
|
||||
export async function seedEdukasiLingkungan() {
|
||||
for (const e of tujuanEdukasiLingkungan) {
|
||||
await prisma.tujuanEdukasiLingkungan.upsert({
|
||||
where: {
|
||||
id: e.id,
|
||||
},
|
||||
update: {
|
||||
judul: e.judul,
|
||||
deskripsi: e.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: e.id,
|
||||
judul: e.judul,
|
||||
deskripsi: e.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("tujuan edukasi lingkungan success ...");
|
||||
|
||||
for (const m of materiEdukasiLingkungan) {
|
||||
await prisma.materiEdukasiLingkungan.upsert({
|
||||
where: {
|
||||
id: m.id,
|
||||
},
|
||||
update: {
|
||||
judul: m.judul,
|
||||
deskripsi: m.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: m.id,
|
||||
judul: m.judul,
|
||||
deskripsi: m.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("materi edukasi lingkungan success ...");
|
||||
|
||||
for (const c of contohEdukasiLingkungan) {
|
||||
await prisma.contohEdukasiLingkungan.upsert({
|
||||
where: {
|
||||
id: c.id,
|
||||
},
|
||||
update: {
|
||||
judul: c.judul,
|
||||
deskripsi: c.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: c.id,
|
||||
judul: c.judul,
|
||||
deskripsi: c.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("contoh edukasi lingkungan success ...");
|
||||
}
|
||||
63
prisma/_seeder_list/lingkungan/seed_konservasi_adat_bali.ts
Normal file
63
prisma/_seeder_list/lingkungan/seed_konservasi_adat_bali.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import filosofiTriHita from "../../data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json";
|
||||
import bentukKonservasiBerdasarkanAdat from "../../data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json";
|
||||
import nilaiKonservasiAdat from "../../data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
|
||||
|
||||
export async function seedKonservasiAdatBali() {
|
||||
for (const f of filosofiTriHita) {
|
||||
await prisma.filosofiTriHita.upsert({
|
||||
where: {
|
||||
id: f.id,
|
||||
},
|
||||
update: {
|
||||
judul: f.judul,
|
||||
deskripsi: f.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: f.id,
|
||||
judul: f.judul,
|
||||
deskripsi: f.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("filosofi tri hita success ...");
|
||||
|
||||
for (const b of bentukKonservasiBerdasarkanAdat) {
|
||||
await prisma.bentukKonservasiBerdasarkanAdat.upsert({
|
||||
where: {
|
||||
id: b.id,
|
||||
},
|
||||
update: {
|
||||
judul: b.judul,
|
||||
deskripsi: b.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: b.id,
|
||||
judul: b.judul,
|
||||
deskripsi: b.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("bentuk konservasi berdasarkan adat success ...");
|
||||
|
||||
for (const n of nilaiKonservasiAdat) {
|
||||
await prisma.nilaiKonservasiAdat.upsert({
|
||||
where: {
|
||||
id: n.id,
|
||||
},
|
||||
update: {
|
||||
judul: n.judul,
|
||||
deskripsi: n.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: n.id,
|
||||
judul: n.judul,
|
||||
deskripsi: n.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("nilai konservasi adat success ...");
|
||||
}
|
||||
51
prisma/_seeder_list/lingkungan/seed_pengelolaan_sampah.ts
Normal file
51
prisma/_seeder_list/lingkungan/seed_pengelolaan_sampah.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import pengelolaanSampah from "../../data/lingkungan/pengelolaan-sampah/pengelolaan-sampah.json";
|
||||
import keteranganBankSampah from "../../data/lingkungan/pengelolaan-sampah/keterangan-bank-sampah.json";
|
||||
|
||||
export async function seedPengelolaanSampah() {
|
||||
console.log("🔄 Seeding Pengelolaan Sampah...");
|
||||
for (const p of pengelolaanSampah) {
|
||||
await prisma.pengelolaanSampah.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
icon: p.icon,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
icon: p.icon,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Pengelolaan Sampah seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Keterangan Bank Sampah...");
|
||||
for (const p of keteranganBankSampah) {
|
||||
await prisma.keteranganBankSampahTerdekat.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
alamat: p.alamat,
|
||||
namaTempatMaps: p.namaTempatMaps,
|
||||
linkPetunjukArah: p.linkPetunjukArah,
|
||||
lat: p.lat,
|
||||
lng: p.lng,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
alamat: p.alamat,
|
||||
namaTempatMaps: p.namaTempatMaps,
|
||||
linkPetunjukArah: p.linkPetunjukArah,
|
||||
lat: p.lat,
|
||||
lng: p.lng,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Keterangan Bank Sampah seeded successfully");
|
||||
}
|
||||
27
prisma/_seeder_list/lingkungan/seed_program_penghijauan.ts
Normal file
27
prisma/_seeder_list/lingkungan/seed_program_penghijauan.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import programPenghijauan from "../../data/lingkungan/program-penghijauan/program-penghijauan.json";
|
||||
|
||||
export async function seedProgramPenghijauan() {
|
||||
console.log("🔄 Seeding Program Penghijauan...");
|
||||
for (const p of programPenghijauan) {
|
||||
await prisma.programPenghijauan.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
name: p.name,
|
||||
judul: p.judul,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
judul: p.judul,
|
||||
deskripsi: p.deskripsi,
|
||||
icon: p.icon,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Program Penghijauan seeded successfully");
|
||||
}
|
||||
60
prisma/_seeder_list/pendidikan/seed_bimbingan_belajar.ts
Normal file
60
prisma/_seeder_list/pendidikan/seed_bimbingan_belajar.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import tujuanBimbinganBelajarDesa from "../../data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json";
|
||||
import lokasiJadwalBimbinganBelajarDesa from "../../data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json";
|
||||
import fasilitasBimbinganBelajarDesa from "../../data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json";
|
||||
|
||||
export async function seedBimbinganBelajar() {
|
||||
for (const t of tujuanBimbinganBelajarDesa) {
|
||||
await prisma.tujuanBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ tujuan bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
|
||||
for (const t of lokasiJadwalBimbinganBelajarDesa) {
|
||||
await prisma.lokasiJadwalBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ lokasi jadwal bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
|
||||
for (const t of fasilitasBimbinganBelajarDesa) {
|
||||
await prisma.fasilitasBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
}
|
||||
23
prisma/_seeder_list/pendidikan/seed_data_pendidikan.ts
Normal file
23
prisma/_seeder_list/pendidikan/seed_data_pendidikan.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import dataPendidikan from "../../data/pendidikan/data-pendidikan/data-pendidikan.json";
|
||||
|
||||
export async function seedDataPendidikan() {
|
||||
console.log("🔄 Seeding Data pendidikan...");
|
||||
for (const k of dataPendidikan) {
|
||||
await prisma.dataPendidikan.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
name: k.name,
|
||||
jumlah: k.jumlah,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
name: k.name,
|
||||
jumlah: k.jumlah,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Data pendidikan seeded successfully");
|
||||
}
|
||||
71
prisma/_seeder_list/pendidikan/seed_data_perpustakaan.ts
Normal file
71
prisma/_seeder_list/pendidikan/seed_data_perpustakaan.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import dataPerpustakaan from "../../data/pendidikan/perpustakaan-digital/perpustakaan-digital.json";
|
||||
import kategoriBuku from "../../data/pendidikan/perpustakaan-digital/kategori-buku.json";
|
||||
|
||||
export async function seedDataPerpustakaan() {
|
||||
console.log("🔄 Seeding Kategori Buku...");
|
||||
for (const k of kategoriBuku) {
|
||||
await prisma.kategoriBuku.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
name: k.name,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
name: k.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Kategori Buku seeded successfully");
|
||||
|
||||
console.log("🔄 Seeding Data perpustakaan...");
|
||||
for (const k of dataPerpustakaan) {
|
||||
let imageId: string | null = null;
|
||||
|
||||
if (k.imageName) {
|
||||
const image = await prisma.fileStorage.findUnique({
|
||||
where: { name: k.imageName },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(
|
||||
`⚠️ Image not found for perpustakaan "${k.judul}": ${k.imageName}`,
|
||||
);
|
||||
} else {
|
||||
imageId = image.id;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.dataPerpustakaan.upsert({
|
||||
where: {
|
||||
id: k.id,
|
||||
},
|
||||
update: {
|
||||
judul: k.judul,
|
||||
deskripsi: k.deskripsi,
|
||||
kategoriId: k.kategoriId,
|
||||
imageId: imageId
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
judul: k.judul,
|
||||
deskripsi: k.deskripsi,
|
||||
kategoriId: k.kategoriId,
|
||||
imageId: imageId
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Data perpustakaan seeded successfully");
|
||||
}
|
||||
if (import.meta.main) {
|
||||
seedDataPerpustakaan()
|
||||
.then(() => {
|
||||
console.log("seed data perpustakaan success");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("gagal seed data perpustakaan", JSON.stringify(err));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import tujuanProgram from "../../data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||
import programUnggulan from "../../data/pendidikan/program-pendidikan-anak/program-unggulan.json";
|
||||
|
||||
export async function seedInfoProgramPendidikan() {
|
||||
for (const t of tujuanProgram) {
|
||||
await prisma.tujuanProgram.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ tujuan program seeded (editable later via UI)");
|
||||
for (const t of programUnggulan) {
|
||||
await prisma.programUnggulan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ program unggulan seeded (editable later via UI)");
|
||||
}
|
||||
74
prisma/_seeder_list/pendidikan/seed_info_sekolah.ts
Normal file
74
prisma/_seeder_list/pendidikan/seed_info_sekolah.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import jenjangPendidikan from "../../data/pendidikan/info-sekolah/jenjang-pendidikan.json";
|
||||
import lembagaPendidikan from "../../data/pendidikan/info-sekolah/lembaga.json";
|
||||
import siswa from "../../data/pendidikan/info-sekolah/siswa.json";
|
||||
import pengajar from "../../data/pendidikan/info-sekolah/pengajar.json";
|
||||
|
||||
export async function seedInfoSekolah() {
|
||||
for (const j of jenjangPendidikan) {
|
||||
await prisma.jenjangPendidikan.upsert({
|
||||
where: {
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
nama: j.nama,
|
||||
},
|
||||
create: {
|
||||
id: j.id,
|
||||
nama: j.nama,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Jenjang Pendidikan seeded successfully");
|
||||
for (const j of lembagaPendidikan) {
|
||||
await prisma.lembaga.upsert({
|
||||
where: {
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
nama: j.nama,
|
||||
jenjangId: j.jenjangId,
|
||||
},
|
||||
create: {
|
||||
id: j.id,
|
||||
nama: j.nama,
|
||||
jenjangId: j.jenjangId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Lembaga Pendidikan seeded successfully");
|
||||
for (const j of siswa) {
|
||||
await prisma.siswa.upsert({
|
||||
where: {
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
nama: j.nama,
|
||||
lembagaId: j.lembagaId,
|
||||
},
|
||||
create: {
|
||||
id: j.id,
|
||||
nama: j.nama,
|
||||
lembagaId: j.lembagaId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ siswa seeded successfully");
|
||||
for (const j of pengajar) {
|
||||
await prisma.pengajar.upsert({
|
||||
where: {
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
nama: j.nama,
|
||||
lembagaId: j.lembagaId,
|
||||
},
|
||||
create: {
|
||||
id: j.id,
|
||||
nama: j.nama,
|
||||
lembagaId: j.lembagaId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ pengajar seeded successfully");
|
||||
}
|
||||
60
prisma/_seeder_list/pendidikan/seed_pendidikan_non_formal.ts
Normal file
60
prisma/_seeder_list/pendidikan/seed_pendidikan_non_formal.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import tujuanProgram from "../../data/pendidikan/pendidikan-non-formal/tujuan-program2.json";
|
||||
import tempatKegiatan from "../../data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json";
|
||||
import jenisProgramYangDiselenggarakan from "../../data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
|
||||
|
||||
export async function seedPendidikanNonFormal() {
|
||||
for (const t of tujuanProgram) {
|
||||
await prisma.tujuanPendidikanNonFormal.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
|
||||
for (const t of tempatKegiatan) {
|
||||
await prisma.tempatKegiatan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
|
||||
for (const t of jenisProgramYangDiselenggarakan) {
|
||||
await prisma.jenisProgramYangDiselenggarakan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)",
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"id": "1b7a17ea-83f7-4e73-a94d-96e2b4a623f2",
|
||||
"nama": "Warung Pasar Darmasaba",
|
||||
"harga": 30000,
|
||||
"imageId": "cmkew56ls0000vnysrnzr9ttx",
|
||||
"imageName": "YdCBnK-bWxlyHjwsk4Qie-mobile.webp",
|
||||
"rating": 4.3,
|
||||
"alamatUsaha": "Br. Baler Pasar, Desa Darmasaba, Kec. Abiansemal",
|
||||
"kontak": "081234567890",
|
||||
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"id": "6dea2257-b710-4cd2-8d94-9b6737e658d8",
|
||||
"nama": "Jajanan Pasar Bu Made",
|
||||
"imageId": "cmkewaa2s0001vnysvvs9tu56",
|
||||
"imageName": "TWdNTZZbTOhFTNJGGPDyG-mobile.webp",
|
||||
"harga": 5000,
|
||||
"rating": 4.6,
|
||||
"alamatUsaha": "Jl. Raya Darmasaba, dekat Banjar Baler Pasar",
|
||||
@@ -24,7 +24,7 @@
|
||||
{
|
||||
"id": "24c6b992-49da-4c6e-aebb-72cf89f75438",
|
||||
"nama": "Sayur Segar Pak Wayan",
|
||||
"imageId": "cmkewcvfq0002vnys6985nm90",
|
||||
"imageName": "mtQsaKtQnhxIYVIooCkiQ-mobile.webp",
|
||||
"harga": 20000,
|
||||
"rating": 4.4,
|
||||
"alamatUsaha": "Area Pasar Desa Darmasaba",
|
||||
@@ -35,7 +35,7 @@
|
||||
{
|
||||
"id": "d62660a2-ac6b-428a-acf6-58cc837ef789",
|
||||
"nama": "Ayam & Daging Segar Darmasaba",
|
||||
"imageId": "cmkewf4u90003vnys87en35nj",
|
||||
"imageName": "Ez-SkRyf_F-1gksz_amNg-mobile.webp",
|
||||
"harga": 80000,
|
||||
"rating": 4.2,
|
||||
"alamatUsaha": "Br. Baler Pasar, Desa Darmasaba",
|
||||
|
||||
@@ -3,30 +3,30 @@
|
||||
"id": "cmkkshcox000504l88lp54coc",
|
||||
"name": "Darmasaba Digital App",
|
||||
"deskripsi": "<p>Aplikasi digital desa yang dikembangkan oleh Pemerintah Desa Darmasaba pada tahun 2024 untuk mempermudah pelayanan publik dan informasi pemerintahan berbasis digital.</p>",
|
||||
"imageId": "cmkksb3jr0005vni4sp3ogr87"
|
||||
"imageName": "r_gBF0FuFpFPfSENHc4XI-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkkshln8000604l8c9b5b4il",
|
||||
"name": "D’DAMART (Darmasaba Digital Market)",
|
||||
"deskripsi": "<p>Sistem pasar UMKM digital berbasis website yang dikembangkan untuk meningkatkan akses pasar dan pemasaran produk UMKM Desa Darmasaba melalui platform digital.</p>",
|
||||
"imageId": "cmkksoze80008vni4ki2ry81r"
|
||||
"imageName": "uE2QwpbcXyBWxVYqCWQQT-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm1a1g80007vnsw8ejmj816",
|
||||
"name": "Media Aspirasi dan Pengaduan Warga",
|
||||
"deskripsi": "<p>Media aspirasi dan pengaduan warga disediakan sebagai wadah partisipasi masyarakat dalam menyampaikan saran, masukan, maupun keluhan secara transparan dan terstruktur. Fitur ini memperkuat komunikasi dua arah antara pemerintah desa dan masyarakat, sehingga setiap aspirasi dapat ditindaklanjuti secara lebih cepat dan akuntabel.</p>",
|
||||
"imageId": "cmkm1a14d0005vnsww1tsd92o"
|
||||
"imageName": "c7xWNyoYp8Cak28NG5NoG-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm0w0s50003vnswmwpnqsi5",
|
||||
"name": "Website Desa Resmi",
|
||||
"deskripsi": "<p>Website Desa Darmasaba berfungsi sebagai sarana utama penyampaian informasi resmi kepada masyarakat. Melalui website ini, pemerintah desa menghadirkan keterbukaan informasi publik, mempermudah akses warga terhadap berita, pengumuman, serta agenda kegiatan desa, sekaligus menjadi pusat data dan referensi terkait profil dan struktur pemerintahan desa.</p>",
|
||||
"imageId": "cmkm0z9hx0004vnswtjd2bk3z"
|
||||
"imageName": "kN09yF3sahmy-d5EaeGqA-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm1c8wx000avnswksc56orq",
|
||||
"name": "Publikasi Kegiatan Desa Secara Digital",
|
||||
"deskripsi": "<p>Publikasi kegiatan desa secara digital bertujuan untuk mendokumentasikan dan menyebarluaskan berbagai aktivitas serta program kerja pemerintah desa. Melalui artikel dan dokumentasi foto, masyarakat dapat mengetahui perkembangan kegiatan desa secara terbuka, sekaligus meningkatkan kepercayaan publik terhadap pelaksanaan program desa.</p>",
|
||||
"imageId": "cmkm1c8py0008vnsw0unbxkpq"
|
||||
"imageName": "h_Gd0SoeIJVTi_5TWUO-P-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,24 +3,24 @@
|
||||
"id": "cmkm2xlqr000mvnswdaymiho6",
|
||||
"name": "Darmasaba Digital App",
|
||||
"deskripsi": "<p>Aplikasi layanan desa berbasis teknologi untuk transparansi informasi dan layanan publik di Desa Darmasaba yang membantu warga mendapatkan informasi administratif, berita desa, dan pelayanan digital lainnya secara cepat dan mudah.</p>",
|
||||
"imageId": "cmkm3bnkt000qvnswzhqa4upf"
|
||||
"imageName": "xVrwJgdwtcoABPU6DB__Y-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm3b1fw000pvnswpr7hgzhp",
|
||||
"name": "Program Digitalisasi Desa",
|
||||
"deskripsi": "<p>Program kerja sama Desa Darmasaba bersama PT. Bali Interaktif Perkasa untuk memperkuat kapasitas pemanfaatan teknologi informasi dan komunikasi dalam administrasi desa, pelayanan publik, serta pemberdayaan digital masyarakat.</p>",
|
||||
"imageId": "cmkm3b1a2000nvnswb9x48dzk"
|
||||
"imageName": "JjUDrfqxuEMYSAza-s7A8-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm3fwmq000tvnswejmhm7yc",
|
||||
"name": "Pengembangan Sistem Informasi Desa",
|
||||
"deskripsi": "<p>Inisiatif pengembangan Sistem Informasi Desa yang mendukung pengelolaan data desa secara digital, termasuk data publik, laporan, dan statistik warga, sebagai bagian dari peningkatan kapabilitas teknologi informasi desa.</p>",
|
||||
"imageId": "cmkm3fwg4000rvnsw5d1vbiz0"
|
||||
"imageName": "42RCCpBZla4ZWxXcwx7kG-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm3hjp6000wvnswkuylnf53",
|
||||
"name": "Pelayanan Kependudukan Berbasis Digital",
|
||||
"deskripsi": "<p>Program untuk menyediakan layanan kependudukan secara digital, termasuk integrasi sistem administrasi kependudukan desa dengan sistem nasional, guna mempercepat layanan e-KTP, kartu keluarga, dan berkas kependudukan lainnya.</p>",
|
||||
"imageId": "cmkm3hjhz000uvnswwqu6z9f6"
|
||||
"imageName": "TrbkwnYM5rKZeHlISHCX4-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
{
|
||||
"id": "cmkm1ziyi000dvnsweg8lp3f7",
|
||||
"name": "TP Posyandu Bali",
|
||||
"imageId": "cmkm1zis2000bvnsw85m6wdlf"
|
||||
"imageName": "qJFWokQLCaO60j0XJU_33-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm1ziyi000dvnsweg8lq4g8",
|
||||
"name": "BRI Peduli",
|
||||
"imageId": "cmkm2dgif000evnswskk0dfo9"
|
||||
"imageName": "nzLJoEAfl7HkpUcYa8Y1E-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkm1ziyi000dvnsweg8lr5h9",
|
||||
"name": "Universitas Warmadewa (KKN-PMM)",
|
||||
"imageId": "cmkm2fzub000hvnswnvoytlzs"
|
||||
"imageName": "JFd5C2FoaZcgDQUmvp-AO-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"deskripsiPengaduan": "<p>Permintaan Pemasangan Spanduk Larangan Bagi Hewan</p>",
|
||||
"lokasiKejadian": "Banjar Darmasaba Tengah",
|
||||
"jenisPengaduanId": "eommt91ma000004lb4dpq7ll1",
|
||||
"imageId": "cmkkxep9l000evni4xkegbk72"
|
||||
"imageName": "gyNi4s8TnK2UrViU-gN2C-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkkrxmub0004vni41cwyhid5",
|
||||
@@ -21,7 +21,7 @@
|
||||
"deskripsiPengaduan": "<p>Laporan Anjing Liar Sering Menyerang Warga</p>",
|
||||
"lokasiKejadian": "Jl. Raya Darmasaba",
|
||||
"jenisPengaduanId": "eommt91ma000004lb4dpq8mm2",
|
||||
"imageId": "cmkkx9e38000bvni4azjd3u53"
|
||||
"imageName": "SQqSobKRg3ShvgPw_H41h-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkkrxmub0004vni41cwyhid6",
|
||||
@@ -33,6 +33,6 @@
|
||||
"deskripsiPengaduan": "<p>Pengelolaan Sampah Rumah Tangga Belum Efektif</p>",
|
||||
"lokasiKejadian": "Banjar Bucu",
|
||||
"jenisPengaduanId": "eommt91ma000004lb4dpq7ll1",
|
||||
"imageId": "cmkky60sq0000vnjjc55k84d2"
|
||||
"imageName": "y78xZ2axTOjz87gRKjVAf-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
"id": "cmkc2tcs00002vnt9c0ssj05n",
|
||||
"name": "Sosialisasi dan Pembinaan Keamanan Lingkungan Desa Darmasaba",
|
||||
"deskripsi": "<p>Pemerintah Desa Darmasaba melaksanakan Sosialisasi dan Pembinaan tentang keamanan dan ketertiban lingkungan kepada warga Perumahan Darmasaba Permai di Wantilan Perum Darmasaba Permai, Desa Darmasaba. Kegiatan ini melibatkan Perbekel Darmasaba, Bhabinkamtibmas, Babinsa, anggota BPD, LPM Desa, KBD dan KBA untuk mengajak warga berperan aktif dalam menjaga keamanan lingkungan, serta mendukung pemasangan lampu penerangan jalan guna mencegah kriminalitas dan kecelakaan di wilayah lingkungan.</p>",
|
||||
"imageId": "cmkc2tcn30000vnt9esmx8kyb"
|
||||
"imageName": "K0wY911212dinYA3AFB_f-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkc2xmdh0005vnt9ri6f4nk8",
|
||||
"name": "Sinergi Aparat dan Masyarakat untuk Keamanan Lingkungan",
|
||||
"deskripsi": "<p>Desa Darmasaba bersama aparat seperti Polres Badung dan elemen masyarakat berkomitmen menjalin sinergi untuk menciptakan keamanan dan ketertiban lingkungan yang kondusif, memperkuat kepedulian serta tindakan nyata dalam menjaga situasi kamtibmas desa.</p>",
|
||||
"imageId": "cmkc2xm1z0003vnt98682dv0a"
|
||||
"imageName": "x0_-siY2V8IehBzo4_uph-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkc36qbl0008vnt9odvekex6",
|
||||
"name": "Peran Sistem Keamanan Lingkungan (Siskamling) dan Pecalang di Bali",
|
||||
"deskripsi": "<p>Sistem keamanan lingkungan (Siskamling) di Bali termasuk di Desa Darmasaba melibatkan kolaborasi antara pemerintah desa, satlinmas, dan pecalang sebagai pranata adat Bali. Sinergi ini penting untuk menjaga ketertiban masyarakat serta harmoni sosial berdasarkan kearifan lokal seperti Tri Hita Karana, meskipun perlu pembinaan dan koordinasi terus menerus dari desa dan aparat terkait.</p>",
|
||||
"imageId": "cmkc36q2j0006vnt9g87h5it4"
|
||||
"imageName": "TXknK9CSRSxwvM2hPW6BO-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
"id": "cmkp70zau0002vnu9o1jtpi1i",
|
||||
"judul": "Keamanan Rumah",
|
||||
"deskripsi": "<p><ul><li><p>Pastikan pintu dan jendela selalu terkunci saat meninggalkan rumah</p></li><li><p>Pasang lampu penerangan di halaman dan area sekitar rumah untuk mencegah tindak kejahatan.</p></li><li><p>Jangan mudah memberikan akses masuk ke orang yang tidak dikenal.</p></li></ul></p>",
|
||||
"imageId": "cmkp71pub0003vnu9ef60huuv"
|
||||
"imageName": "dSe0xyvNLkP2t2f6iq-Hk-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkp71pzo0005vnu9p3n9646d",
|
||||
"judul": "Keamanan Lingkungan Tanggungjawab Bersama",
|
||||
"deskripsi": "<p>Pemerintah Desa Darmasaba melaksanakan sosialisasi dan pembinaan tentang keamanan dan ketertiban lingkungan kepada warga Perumahan Darmasaba Permai. Warga diajak berperan aktif dalam menjaga keamanan lingkungan serta mendukung penyediaan lampu penerangan jalan untuk mencegah tindak kriminal dan kecelakaan. Bhabinkamtibmas dan Babinsa turut memberikan materi keamanan dan ketertiban kepada warga, menekankan pentingnya partisipasi masyarakat dalam menjaga keamanan desa.</p>",
|
||||
"imageId": "cmkp70z5g0000vnu9b0aieem8"
|
||||
"imageName": "vwZsaxcoFWDlxG1PW7FC0-mobile.webp"
|
||||
}
|
||||
]
|
||||
@@ -4,34 +4,34 @@
|
||||
"name": "Diare dan Kolera",
|
||||
"deskripsiSingkat": "<p>Apa itu Diare dan Kolera penyebab, gejala dan cara penanganannya?</p><p>Yuk Kenali gelaja dan cara penanganan Diare dan Kolera yang efektif untuk melindungi keluarga anda.</p>",
|
||||
"deskripsiLengkap": "<p>Apa itu Diare dan Kolera penyebab, gejala dan cara penanganannya?</p><p>Yuk Kenali gelaja dan cara penanganan Diare dan Kolera yang efektif untuk melindungi keluarga anda.</p><ul><li><p>Penyebab: Bakteri Vibrio cholerae (Kolera) atau Escherichia coli (diare) akibat makanan/minuman yang terkontaminasi.</p></li><li><p>Gejala: Buang air besar cair terus-menerus, dehidrasi, dan lemas. Pencegahan: Menjaga kebersihan makanan dan air, serta mencuci tangan dengan sabun.</p></li></ul>",
|
||||
"imageId": "cmkax3o8g000rvn6ygqpmo1nb"
|
||||
"imageName": "5giLSHSnWEFoZoMEcjhL7-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkax5urc000wvn6yxfw0970w",
|
||||
"name": "TBC (Tuberkulosis)",
|
||||
"deskripsiSingkat": "<p>Apa itu TBC penyebab, gejala dan cara penanganannya?</p><p>Yuk Kenali gelaja dan cara penanganan TBC yang efektif untuk melindungi keluarga anda.</p>",
|
||||
"deskripsiLengkap": "<p>Apa itu TBC penyebab, gejala dan cara penanganannya?</p><p>Yuk Kenali gelaja dan cara penanganan TBC yang efektif untuk melindungi keluarga anda.</p><p>Penyebab: Bakteri Mycobacterium tuberculosis yang menyebar melalui udara.</p><p>Gejala: Batuk lebih dari 2 minggu, berkeringat di malam hari, dan berat badan turun.</p><p>Pencegahan: Vaksin BCG, pola hidup sehat, dan pengobatan bagi penderita agar tidak menular.</p>",
|
||||
"imageId": "cmkax5ukz000uvn6yho3aj2nf"
|
||||
"imageName": "3faPo-1wjhVDVU6S7S8sS-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkax72s7000zvn6yz3nmvrry",
|
||||
"name": "Demam Berdarah Dengue (DBD)",
|
||||
"deskripsiSingkat": "<p>Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.</p>",
|
||||
"deskripsiLengkap": "<p>Apa itu DBD penyebab, gejala dan cara penanganannya?</p><p>Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.</p><p>Penyebab: Virus dengue yang ditularkan oleh nyamuk Aedes aegypti.</p><p>Gejala: Demam tinggi, nyeri sendi, ruam kulit, dan pendarahan ringan.</p><p>Pencegahan: Menguras tempat air, menutup wadah air, fogging, dan menggunakan lotion anti-nyamuk.</p>",
|
||||
"imageId": "cmkax72nw000xvn6ymcuvlzom"
|
||||
"imageName": "DyX82oztXbHfu6HEvbrpt-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkbyny4f0002vn67kmjmjrpl",
|
||||
"name": "Fogging sebagai Pencegah DBD di Br. Umahanyar Desa Darmasaba",
|
||||
"deskripsiSingkat": "<p>Pemerintah Desa Darmasaba melaksanakan fogging di wilayah Br. Umahanyar sebagai upaya pencegahan DBD di Desa Darmasaba.</p>",
|
||||
"deskripsiLengkap": "<p>Pemerintah Desa Darmasaba melaksanakan fogging (pengasapan) di wilayah Br. Umahanyar Desa Darmasaba Kecamatan Abiansemal Kabupaten Badung dari tanggal 12 sampai dengan 13 April 2023.</p><p>Fogging ini merupakan salah satu metode yang dilakukan oleh Pemdes Darmasaba dalam pencegahan penyakit Demam Berdarah Dengue (DBD) dengan menargetkan nyamuk Aedes aegypti sebagai vektor penyebabnya.</p>",
|
||||
"imageId": "cmkbynxxo0000vn67wi2nsyl3"
|
||||
"imageName": "pps1ZgzJxDb4VZxEvtZeu-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkbyr3rx0005vn674uhycsxc",
|
||||
"name": "Gerakan Serentak Penyemprotan Pencegahan PMK di Desa Darmasaba",
|
||||
"deskripsiSingkat": "<p>Penyemprotan serentak dilakukan di Desa Darmasaba untuk mencegah Penyakit Mulut dan Kaki (PMK) pada hewan ternak.</p>",
|
||||
"deskripsiLengkap": "<p>Setelah dilakukan vaksinasi Penyakit Mulut dan Kaki (PMK) pada hewan ternak yaitu sapi di wilayah Desa Darmasaba, Pemerintah Desa Darmasaba melaksanakan gerakan serentak penyemprotan pencegahan PMK pada hari Rabu (20/7/2022) di seputaran wilayah Desa Darmasaba.</p><p>Upaya ini dilakukan sebagai bentuk pencegahan terhadap penyebaran PMK dan menjaga kesehatan hewan ternak di desa.</p>",
|
||||
"imageId": "cmkbyr3mk0003vn673xrqv8xv"
|
||||
"imageName": "JhJigMo269K1TFGzSB1OS-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,28 +3,28 @@
|
||||
"id": "cmkax1vks000qvn6yyxuvfsi8",
|
||||
"name": "Puskesmas Pembantu Darmasaba",
|
||||
"deskripsi": "<p>Puskesmas Pembantu Darmasaba merupakan fasilitas kesehatan tingkat pertama yang berada di Desa Darmasaba, melayani berbagai layanan kesehatan masyarakat termasuk pemeriksaan umum dan imunisasi.</p>",
|
||||
"imageId": "cmkb6488i001fvn6ylkddch1j",
|
||||
"imageName": "g4ICsRrmOaIqS_yqlQLZK-mobile.webp",
|
||||
"whatsapp": "089647037430"
|
||||
},
|
||||
{
|
||||
"id": "cmkawzrvg000nvn6ywyx529em",
|
||||
"name": "UPTD Puskesmas Abiansemal III (melayani Darmasaba)",
|
||||
"deskripsi": "<p>Puskesmas Abiansemal III adalah fasilitas kesehatan utama di kecamatan Abiansemal yang melayani wilayah Desa Darmasaba dan sekitarnya. Puskesmas ini memiliki layanan 24 jam serta pelayanan darurat kesehatan dasar.</p>",
|
||||
"imageId": "cmkb681og001gvn6ykb5uasln",
|
||||
"imageName": "1NkzPzQailqE5yNOiUjB9-mobile.webp",
|
||||
"whatsapp": "03618463263"
|
||||
},
|
||||
{
|
||||
"id": "cmkawy5in000kvn6yza82pkkg",
|
||||
"name": "UPTD Puskesmas Abiansemal I",
|
||||
"deskripsi": "<p>Puskesmas Abiansemal I melayani masyarakat di wilayah kecamatan Abiansemal, termasuk pelayanan kesehatan darurat dan program kesehatan masyarakat.</p>",
|
||||
"imageId": "cmkb6brrf0000vn14u8c7wnox",
|
||||
"imageName": "NBPAqjPXn7GQmYTDBI5hu-mobile.webp",
|
||||
"whatsapp": "087858367111"
|
||||
},
|
||||
{
|
||||
"id": "cmkb6ehu20003vn14ca4xr057",
|
||||
"name": "Kantor Desa Darmasaba (Kontak Informasi Kesehatan)",
|
||||
"deskripsi": "<p>Kantor Pemerintahan Desa Darmasaba dapat menjadi saluran kontak awal untuk rujukan layanan kesehatan darurat atau informasi lebih lanjut mengenai fasilitas kesehatan di wilayah desa.</p>",
|
||||
"imageId": "cmkb6ehpi0001vn14hjp4tdye",
|
||||
"imageName": "EcQIGOF6LW1dIKE53vmba-mobile.webp",
|
||||
"whatsapp": "081239580000"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,24 +3,24 @@
|
||||
"id": "cmkawso7y000evn6ygob15cqb",
|
||||
"name": "Rembug Stunting di Desa Darmasaba",
|
||||
"deskripsi": "<p>Pemerintah Desa Darmasaba melaksanakan kegiatan rembug stunting dengan melibatkan bidan desa, kader posyandu, dan tokoh masyarakat. Tujuan kegiatan ini adalah untuk memperkuat upaya pencegahan kekerdilan (stunting) melalui koordinasi layanan kesehatan, edukasi gizi, serta percepatan penanganan gizi buruk di lingkungan desa sebagai bagian dari respons terhadap kondisi kesehatan yang mendesak.</p>",
|
||||
"imageId": "cmkayz2h8001cvn6yrb7uptjs"
|
||||
"imageName": "Gi8EX3pBmT719AfzXirDS-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawq3ef000bvn6y387vub0y",
|
||||
"name": "Posko Kesehatan Darurat dan Bencana",
|
||||
"deskripsi": "<p>Posko Kesehatan Darurat dan Bencana Desa Darmasaba dibentuk sebagai pusat koordinasi dan pertolongan bagi warga yang terdampak situasi darurat seperti banjir, tanah longsor, atau wabah penyakit. Posko ini dilengkapi dengan tenaga medis, obat-obatan dasar, serta dukungan logistik untuk memastikan penanganan cepat dan tepat sasaran. Kegiatan ini juga melibatkan kader kesehatan desa dan karang taruna sebagai relawan lapangan.</p>",
|
||||
"imageId": "cmkawq38m0009vn6yi7evbhap"
|
||||
"imageName": "v7Ac2xQvTiJy-HYh1AxF4-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawso7y000evn6ygob14bpa",
|
||||
"name": "Layanan Ambulans Desa Darmasaba",
|
||||
"deskripsi": "<p>Layanan Ambulans Desa Darmasaba disiapkan untuk membantu masyarakat yang membutuhkan transportasi medis darurat ke fasilitas kesehatan terdekat. Layanan ini beroperasi 24 jam dan dapat dihubungi melalui nomor darurat desa. Tim ambulans terdiri dari relawan terlatih dan tenaga medis yang siap memberikan pertolongan pertama di lokasi kejadian sebelum dirujuk ke rumah sakit atau puskesmas.</p>",
|
||||
"imageId": "cmkawso29000cvn6y879ahra0"
|
||||
"imageName": "jYxEXspWH5g6eTTVqK72c-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawu7te000hvn6yh3pdnv4w",
|
||||
"name": "Penanganan Darurat Sosial & Kesehatan Desa Darmasaba",
|
||||
"deskripsi": "<p>Program Penanganan Darurat Sosial & Kesehatan Desa Darmasaba bertujuan memberikan respon cepat terhadap situasi darurat seperti warga sakit mendadak, kecelakaan, bencana alam, maupun kondisi sosial yang membutuhkan bantuan segera. Tim Siaga Desa Darmasaba berkoordinasi dengan Puskesmas Abiansemal dan BPBD untuk memastikan penanganan yang cepat, tepat, dan manusiawi. Program ini juga mencakup layanan ambulans desa, posko kesehatan darurat, serta bantuan logistik bagi warga terdampak.</p>",
|
||||
"imageId": "cmkawu7qj000fvn6yubhimyiv"
|
||||
"imageName": "3tNQ9J8I3Ewq5H8CWuqvp-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
"nomor": "(0361) 8463263",
|
||||
"deskripsi": "<p>Posyandu Pudak Amara merupakan salah satu posyandu aktif di Desa Darmasaba dan pernah berkompetisi dalam lomba kader dan posyandu berprestasi tingkat Provinsi Bali tahun 2025.</p><p>Kegiatan ini melibatkan kader posyandu serta didampingi pihak desa dan puskesmas setempat untuk meningkatkan pelayanan kesehatan ibu dan anak.</p>",
|
||||
"jadwalPelayanan": "<p>Setiap bulan pada satu hari tertentu (mis. minggu ke-2): 08:00 – 12:00 WITA (posyandu balita & ibu hamil)</p>",
|
||||
"imageId": "cmkanjnfh0004vntz8cdbxa7f"
|
||||
"imageName": "TDQReg1lQ73s39crXW0ra-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,48 +4,48 @@
|
||||
"name": "Gerakan Kulkul PKK dan Posyandu Desa Darmasaba",
|
||||
"deskripsiSingkat": "<p>Kegiatan bersama PKK dan Posyandu untuk meningkatkan pelayanan kesehatan masyarakat.</p>",
|
||||
"deskripsi": "<p>Pada hari Minggu, 11 Januari 2025, Pemerintah Desa Darmasaba melalui TP PKK dan TP Posyandu melaksanakan kegiatan Gerakan Kulkul PKK dan Posyandu yang berlangsung serentak di seluruh wilayah Desa Darmasaba untuk memperkuat pelayanan kesehatan dasar dan peningkatan partisipasi masyarakat dalam program Posyandu.</p>",
|
||||
"imageId": "cmkay1e590010vn6y24pgaa1r"
|
||||
"imageName": "hLeF0GRFZqDUngZnDMAAk-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawmlg40005vn6yja2xiev0",
|
||||
"name": "Pendampingan Kunjungan Rumah oleh Puskesmas Abiansemal 3",
|
||||
"deskripsiSingkat": "<p>Pendataan kesehatan penyandang disabilitas lewat kunjungan rumah di Desa Darmasaba.</p>",
|
||||
"deskripsi": "<p>Pemerintah Desa Darmasaba bersama Kelian Banjar Dinas dan kader kesehatan mendampingi kegiatan kunjungan rumah yang dilaksanakan oleh Puskesmas Abiansemal 3 pada 21 Juli 2025, difokuskan pada pendataan dan pemantauan kondisi kesehatan penyandang disabilitas di Banjar Bersih, Desa Darmasaba.</p>",
|
||||
"imageId": "cmkay6hob0011vn6ybjwejcej"
|
||||
"imageName": "hyyTFi8EApjzFEZ9EvJgB-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawnr9k0008vn6ymwv0foiv",
|
||||
"name": "Kegiatan Aksi Sosial Tim Penggerak Posyandu Provinsi Bali di Desa Darmasaba",
|
||||
"deskripsiSingkat": "<p>Aksi sosial TP Posyandu Bali untuk memperkuat pelayanan posyandu di desa.</p>",
|
||||
"deskripsi": "<p>Pada 10 Desember 2025, Desa Darmasaba menjadi lokasi pelaksanaan Aksi Sosial Tim Penggerak Posyandu Provinsi Bali yang bertujuan memperkuat pelayanan Posyandu serta meningkatkan kesejahteraan masyarakat, khususnya keluarga dan balita.</p>",
|
||||
"imageId": "cmkay8vmd0012vn6ylsk2vzfo"
|
||||
"imageName": "l4qsUEw2JiclGAkkrXp9g-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawnr9k0008vn6ymwv0dpjw",
|
||||
"name": "Inovasi BAJRA dalam Penanggulangan Rabies",
|
||||
"deskripsiSingkat": "<p>Program BAJRA untuk penanggulangan rabies di Desa Darmasaba.</p>",
|
||||
"deskripsi": "<p>Desa Darmasaba mengembangkan inovasi BAJRA (Bersama Jaga Rabies), sebuah program berbasis komunitas untuk penanggulangan rabies yang mengintegrasikan pelaporan cepat masyarakat, edukasi berkelanjutan dan koordinasi lintas sektor antara kesehatan hewan, manusia, dan pemerintahan desa.</p>",
|
||||
"imageId": "cmkayd8o90013vn6ye7n8805q"
|
||||
"imageName": "Gc79mlIlGuoRQuTqskFj--mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawnr9k0008vn6ymwv0eqkx",
|
||||
"name": "Posyandu Pudak Amara Berkompetisi",
|
||||
"deskripsiSingkat": "<p>Partisipasi Posyandu Pudak Amara dalam lomba prestasi Posyandu tingkat provinsi.</p>",
|
||||
"deskripsi": "<p>Kader Posyandu Pudak Amara Br. Cabe mendapat pendampingan dari Perbekel Darmasaba, Dinas Kesehatan Kab. Badung, Puskesmas Abiansemal III, dan Pustu Desa Darmasaba dalam ajang lomba kader dan Posyandu berprestasi tingkat Provinsi Bali tahun 2025.</p>",
|
||||
"imageId": "cmkayi0x90016vn6ykddxqyq3"
|
||||
"imageName": "OsMY3AYPyGC_CoN1xUjOn-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkawnr9k0008vn6ymwv1frly",
|
||||
"name": "Outbound Kader Posyandu Darmasaba",
|
||||
"deskripsiSingkat": "<p>Program pembinaan dan pengembangan kapasitas kader Posyandu.</p>",
|
||||
"deskripsi": "<p>Pemdes Darmasaba melaksanakan kegiatan Outbound Posyandu untuk meningkatkan kapasitas dan wawasan Kader Posyandu se-Desa Darmasaba sebagai bagian dari upaya peningkatan kualitas pelayanan kesehatan dasar di masyarakat.</p>",
|
||||
"imageId": "cmkaykipf0019vn6yknjno3k1"
|
||||
"imageName": "M9QlgVKIEfCdY3g4F_tRZ-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkdu8ki10004vn4lpbxm2zqo",
|
||||
"name": "PEMBANGUNAN JAMBAN BAGI MASYARAKAT",
|
||||
"deskripsiSingkat": "<p>Program pengadaan jamban bagi Masyarakat ini diharapkan menjadi stimulus agar masyarakat peduli terhadap lingkungan sehat sehingga Badung Open Defection Free atau terbebas dari buang air besar di tempat terbuka dapat terwujud.</p>",
|
||||
"deskripsi": "<p>Desa Darmasaba sebagai desa yang berkomitmen selalu selaras dengan pembangunan Pemerintah Kabupaten Badung pada tahun anggaran 2023 ini turut ambil bagian dalam menyukseskan program Bupati Badung I Nyoman Giri Prasta, S.Sos dalam bidang kesehatan sanitasi masyarakat. Program pengadaan jamban bagi Masyarakat ini diharapkan menjadi stimulus agar masyarakat peduli terhadap lingkungan sehat sehingga Badung Open Defection Free atau terbebas dari buang air besar di tempat terbuka dapat terwujud.</p><p style=\"text-align: justify\">Pemberian bantuan jamban ini dilaksanakan di 11 banjar dengan menyasar 22 keluarga yang memang belum memiliki jamban yang sumber dananya sepenuhnya dari APBDes Darmasaba T. A. 2023. Pembangunan Jamban bagi Masyarakat ini juga menjadi bukti komitmen Pemerintah Desa Darmasaba dalam melaksanakan salah satu visi mewujudkan masyarakat yang sejahtera dan berbudaya untuk menjaga lingkungan yang bersih dan sehat.</p>",
|
||||
"imageId": "cmkdu8kb20002vn4lihwo4k86"
|
||||
"imageName": "6DQbAvn0St-xHdPGW3vpY-mobile.webp"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Puskesmas Abiansemal III",
|
||||
"alamat": "Jl. Ratna, Sibang Kaja, Abiansemal, Badung, Bali 80352",
|
||||
"jamId": "cmkao2zwx0008vntzmvqdsdzo",
|
||||
"imageId": "cmkao2zm90007vntzxqkjy5mt",
|
||||
"imageName": "d6hJgycQawWN3VEcHaqtR-mobile.webp",
|
||||
"kontakId": "cmkao2zxc0009vntz00kev051"
|
||||
},
|
||||
{
|
||||
@@ -12,7 +12,7 @@
|
||||
"name": "Puskesmas Pembantu Darmasaba",
|
||||
"alamat": "Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali",
|
||||
"jamId": "cmkao2zwx0008vntzmvqdseal",
|
||||
"imageId": "cmkatoru10000vny38y0wxd6s",
|
||||
"imageName": "cg78Sb_QzZFlli9s2FPVc-mobile.webp",
|
||||
"kontakId": "cmkao2zxc0009vntz00kev162"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"tanggal": "2024-01-28T00:00:00.000Z",
|
||||
"lokasi": "Pura Desa dan Pura Dalem, Desa Adat Tegal, Desa Darmasaba, Badung",
|
||||
"partisipan": 30,
|
||||
"imageId": "cmknb59md0000vnmam828iuzt",
|
||||
"imageName": "YgOX5qAP3O1PHG5XmQXkr-mobile.webp",
|
||||
"kategoriKegiatanId": "cmknan39v000004l8eiql149r"
|
||||
},
|
||||
{
|
||||
@@ -18,7 +18,7 @@
|
||||
"tanggal": "2023-11-17T00:00:00.000Z",
|
||||
"lokasi": "Desa Darmasaba, Badung",
|
||||
"partisipan": 25,
|
||||
"imageId": "cmknbp3vd0001vnmarjz542o7",
|
||||
"imageName": "qxqSDHe-akIRi1EkQFUbG-mobile.webp",
|
||||
"kategoriKegiatanId": "cmknan39v000004l8eiql149r"
|
||||
},
|
||||
{
|
||||
@@ -29,7 +29,7 @@
|
||||
"tanggal": "2022-05-26T00:00:00.000Z",
|
||||
"lokasi": "Pura Dalem Kangin, Desa Adat Tegal, Desa Darmasaba, Badung",
|
||||
"partisipan": 28,
|
||||
"imageId": "cmknbrj4r0002vnmantw9rn0l",
|
||||
"imageName": "iHTVkQZ1VdkMOXLt5qdAd-mobile.webp",
|
||||
"kategoriKegiatanId": "cmknan39v000004l8eiql149r"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,139 +4,195 @@
|
||||
"judul": "Laskar Pelangi",
|
||||
"deskripsi": "<p>Novel inspiratif tentang perjuangan anak-anak di Belitung dalam meraih pendidikan dan mimpi mereka</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq76bdzu",
|
||||
"imageId": "cmkqhbhxi0000vneamj3din9u"
|
||||
"imageName": "RnAdv7O0QAFrxkFLAXJSa-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqhedff0005vneas3rtbumi",
|
||||
"judul": "Bumi Manusia",
|
||||
"deskripsi": "<p>Kisah kehidupan Minke di masa kolonial yang menggambarkan perjuangan, pendidikan, dan identitas bangsa</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqc7bdzu",
|
||||
"imageId": "cmkqhed8x0003vneakx0c7me2"
|
||||
"imageName": "71eZShq4FYAFLxpLfZB0W-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqhg1g70008vneajbpz8phh",
|
||||
"judul": "Atomic Habits",
|
||||
"deskripsi": "<p>Panduan membangun kebiasaan kecil yang konsisten untuk menghasilkan perubahan besar dalam hidup</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqf7bdzu",
|
||||
"imageId": "cmkqhg1cb0006vneagsxa6t4t"
|
||||
"imageName": "Uxq3GXPqh7HN9fHmRkr3r-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqhl6sr000bvneampx0svus",
|
||||
"judul": "Clean Code",
|
||||
"deskripsi": "<p>Buku wajib programmer tentang cara menulis kode yang bersih, mudah dibaca, dan mudah dirawat</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqd7bdzu",
|
||||
"imageId": "cmkqhl6mv0009vneasgix42ud"
|
||||
"imageName": "W5Fc0uRADNkIY3nZicvQA-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqhoaa1000evnearppgpyxo",
|
||||
"judul": "Sejarah Indonesia Modern",
|
||||
"deskripsi": "<p>Membahas perjalanan sejarah Indonesia dari masa kolonial hingga era modern</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqc7bdzu",
|
||||
"imageId": "cmkqhoa5w000cvneah15n28zq"
|
||||
"imageName": "mp77Op-MwtPQZnH3so4JY-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqhr9oc000hvnea677ad3kb",
|
||||
"judul": "Ensiklopedia Anak Pintar",
|
||||
"deskripsi": "<p>Buku referensi bergambar yang membantu anak mengenal ilmu pengetahuan secara menyenangkan</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqh7bdzu",
|
||||
"imageId": "cmkqhr9lg000fvneai3q8qw0s"
|
||||
"imageName": "V09ZxN1wOwbSFLQiDK0VQ-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqi5ksf000kvnea9c04n2hy",
|
||||
"judul": "Filosofi Teras",
|
||||
"deskripsi": "<p>Pengenalan filsafat Stoikisme untuk menghadapi kehidupan modern dengan lebih tenang</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq87bdzu",
|
||||
"imageId": "cmkqi5knc000ivnea8grp7j06"
|
||||
"imageName": "Wqp4AyVkGjqRMED9Q5XAs-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqi97hq000nvneaparjbcrm",
|
||||
"judul": "Pemrograman JavaScript Dasar",
|
||||
"deskripsi": "<p>Panduan dasar belajar JavaScript untuk pemula dalam dunia pengembangan web</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqd7bdzu",
|
||||
"imageId": "cmkqi9799000lvneamskmvpq5"
|
||||
"imageName": "NH4aLc7cVuutdQBCofTC0-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqibjt9000qvnea13ox7fmv",
|
||||
"judul": "Pendidikan Karakter",
|
||||
"deskripsi": "<p>Buku yang membahas pentingnya pendidikan karakter dalam membentuk generasi bangsa</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqf7bdzu",
|
||||
"imageId": "cmkqibjj2000ovnea3zmmvdop"
|
||||
"imageName": "MLrsPrD6oiHsrNP4Lc8J7-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqidnar000tvneaohk5v8k6",
|
||||
"judul": "Psikologi Kepribadian",
|
||||
"deskripsi": "<p>Mengenal teori-teori kepribadian manusia dalam perspektif psikologi</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq87bdzu",
|
||||
"imageId": "cmkqidn7e000rvnea5rl58f2e"
|
||||
"imageName": "iaIeNdhuxqltqKP7aZncQ-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqifdiu000wvnea7xd0yi4f",
|
||||
"judul": "Ayat-Ayat Cinta",
|
||||
"deskripsi": "<p>Novel religi yang mengangkat kisah cinta, iman, dan perjuangan hidup</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqe7bdzu",
|
||||
"imageId": "cmkqifdfs000uvneajss8zswp"
|
||||
"imageName": "WUDssJ59pTKE_3IuTiZ2s-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqik7vi000zvneae7d5cq9i",
|
||||
"judul": "Negeri 5 Menara",
|
||||
"deskripsi": "<p>Cerita persahabatan dan perjuangan santri dalam mengejar mimpi hingga ke mancanegara</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq76bdzu",
|
||||
"imageId": "cmkqik7p5000xvnea6krii3vw"
|
||||
"imageName": "RJH_-4_R_nlP7GVEQeD1M-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqinno30012vneac1sgsvis",
|
||||
"judul": "Belajar UI/UX Design",
|
||||
"deskripsi": "<p>Panduan praktis memahami desain antarmuka dan pengalaman pengguna</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqd7bdzu",
|
||||
"imageId": "cmkqinnih0010vneakpjb9egl"
|
||||
"imageName": "9MA-Jx_36uoho2Tg40_G9-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqiqegd0015vneawv5u5tpm",
|
||||
"judul": "Manajemen Waktu Efektif",
|
||||
"deskripsi": "<p>Teknik mengatur waktu agar lebih produktif dan fokus pada hal penting</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqf7bdzu",
|
||||
"imageId": "cmkqiqeb60013vnea2ygrq5rs"
|
||||
"imageName": "dkb7ZWFl28TREVcvH8sWd-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqiurc60018vneavyd3pj9q",
|
||||
"judul": "Dongeng Nusantara",
|
||||
"deskripsi": "<p>Kumpulan dongeng tradisional Indonesia yang sarat pesan moral</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq76bdzu",
|
||||
"imageId": "cmkqiur960016vnea3werdoey"
|
||||
"imageName": "nVj3one6CLuWRd04QnsWo-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqix2kb001bvnea5v81cw7p",
|
||||
"judul": "Ekonomi Makro",
|
||||
"deskripsi": "<p>Pembahasan konsep ekonomi makro secara sistematis dan mudah dipahami</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq87bdzu",
|
||||
"imageId": "cmkqix2go0019vnea8coousvn"
|
||||
"imageName": "AnB7JO4_6tlPTX3ypOVLi-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqiyts2001evneahnk45ry5",
|
||||
"judul": "Seni Berpikir Kritis",
|
||||
"deskripsi": "<p>Buku yang membantu pembaca menghindari kesalahan berpikir dalam pengambilan keputusan</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq87bdzu",
|
||||
"imageId": "cmkqiytnv001cvnea7o2sv1vt"
|
||||
"imageName": "sAyoMERxL6JgFfiO22KPb-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqj0nq0001hvnea06r8m3kj",
|
||||
"judul": "Seni Berpikir Kritis",
|
||||
"deskripsi": "<p>Buku yang membantu pembaca menghindari kesalahan berpikir dalam pengambilan keputusan</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq87bdzu",
|
||||
"imageId": "cmkqj0nn0001fvneaufur3nke"
|
||||
"imageName": "WeA-JP2Ks_32fv1k529vj-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqj37w4001kvnea04n9w2bx",
|
||||
"judul": "Panduan Shalat Lengkap",
|
||||
"deskripsi": "<p>Panduan praktis dan lengkap tentang tata cara shalat sesuai tuntunan</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqe7bdzu",
|
||||
"imageId": "cmkqj37rg001ivneam29fgayr"
|
||||
"imageName": "pxlHu2kDmIprQqC2PuXaL-mobile.webp"
|
||||
},
|
||||
{
|
||||
"id": "cmkqj5qp6001nvnea4xhvluz3",
|
||||
"judul": "Cerita Sains untuk Anak",
|
||||
"deskripsi": "<p>Cerita edukatif yang mengenalkan sains kepada anak dengan bahasa sederhana</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqh7bdzu",
|
||||
"imageId": "cmkqj5ql6001lvnea6p0afr9f"
|
||||
"imageName": "G0iELZb2DhQDCCP5OdzJR-desktop.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fq776000104jscnj58sgm",
|
||||
"judul": "Pedagogy of the Oppressed",
|
||||
"deskripsi": "<p>Klasik pemikiran pendidikan kritis; menggali hubungan guru-murid dan peran pendidikan dalam pembebasan sosial</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq97bdzu",
|
||||
"imageName": "pendidikan-1.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fqurm000204js5p60hkym",
|
||||
"judul": "The Courage to Teach",
|
||||
"deskripsi": "<p>Tentang refleksi diri seorang pendidik; cocok untuk pengajar yang ingin lebih dari sekedar “metode mengajar”</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibq97bdzu",
|
||||
"imageName": "pendidikan-2.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fqurm000204js5p60hkzn",
|
||||
"judul": "A Brief History of Time",
|
||||
"deskripsi": "<p>Penjelasan kosmologi yang terkenal dunia; sains kompleks dibahas dengan bahasa yang bisa dinikmati pembaca umum</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqa7bdzu",
|
||||
"imageName": "ilmiah-1.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fqurm000204js5p60hkao",
|
||||
"judul": "The Selfish Gene",
|
||||
"deskripsi": "<p>Membawa perspektif baru tentang evolusi melalui “gen” sebagai unit seleksi</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqa7bdzu",
|
||||
"imageName": "ilmiah-2.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fx09c000304jshams3xbg",
|
||||
"judul": "A Little Life",
|
||||
"deskripsi": "<p>Novel yang menggambarkan hidup seorang remaja yang mengalami kehidupan yang sangat sulit</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqb7bdzu",
|
||||
"imageName": "drama-1.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fx09c000304jshams3xch",
|
||||
"judul": "Death of a Salesman",
|
||||
"deskripsi": "<p>Drama teater klasik Amerika tentang harapan, keluarga, dan realitas hidup.</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqb7bdzu",
|
||||
"imageName": "drama-2.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fx09c000304jshams3xdi",
|
||||
"judul": "How Not to Die",
|
||||
"deskripsi": "<p>Panduan berbasis penelitian tentang pola makan untuk mencegah dan menangani penyakit.</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqg7bdzu",
|
||||
"imageName": "kesehatan-1.webp"
|
||||
},
|
||||
{
|
||||
"id": "cml7fx09c000304jshams3xej",
|
||||
"judul": "The Body Keeps the Score",
|
||||
"deskripsi": "<p>Fokus pada trauma, otak & tubuh; penting untuk memahami kesehatan mental secara mendalam.</p>",
|
||||
"kategoriId": "cmkqb11mc000104jibqg7bdzu",
|
||||
"imageName": "kesehatan-2.webp"
|
||||
}
|
||||
]
|
||||
|
||||
246
prisma/lib/create_file_share_folder.ts
Normal file
246
prisma/lib/create_file_share_folder.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
// import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||
|
||||
// type CdnItem = {
|
||||
// name: string;
|
||||
// path: string;
|
||||
// cdnUrl: string;
|
||||
// };
|
||||
|
||||
// type DirItem = {
|
||||
// type: "file" | "dir";
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// const BASE_URL = process.env.SEAFILE_BASE_URL!;
|
||||
// const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||
|
||||
// // folder yang dishare (RELATIVE, tanpa slash depan)
|
||||
// const DIR_TARGET = "asset-web";
|
||||
|
||||
// // 🔑 TOKEN DIRECTORY SHARE (/d/{token})
|
||||
// const PUBLIC_SHARE_TOKEN = process.env.SEAFILE_PUBLIC_SHARE_TOKEN!;
|
||||
|
||||
// /**
|
||||
// * Ambil list file dari repo (butuh token sekali)
|
||||
// */
|
||||
// async function getDirItems(): Promise<DirItem[]> {
|
||||
// const token = await getValidAuthToken();
|
||||
|
||||
// // Validasi bahwa semua variabel lingkungan telah diatur
|
||||
// if (!BASE_URL) {
|
||||
// throw new Error('SEAFILE_BASE_URL environment variable is not set');
|
||||
// }
|
||||
|
||||
// if (!REPO_ID) {
|
||||
// throw new Error('SEAFILE_REPO_ID environment variable is not set');
|
||||
// }
|
||||
|
||||
// // Bangun URL dan pastikan valid
|
||||
// const url = `${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`;
|
||||
|
||||
// try {
|
||||
// new URL(url); // Ini akan melempar error jika URL tidak valid
|
||||
// } catch (error) {
|
||||
// throw new Error(`Invalid URL constructed: ${url}. Error: ${error}`);
|
||||
// }
|
||||
|
||||
// const res = await fetch(url, {
|
||||
// headers: {
|
||||
// Authorization: `Token ${token}`,
|
||||
// },
|
||||
// });
|
||||
|
||||
// if (!res.ok) {
|
||||
// const text = await res.text();
|
||||
// throw new Error(`Failed get dir items: ${text}`);
|
||||
// }
|
||||
|
||||
// return res.json();
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Build PUBLIC CDN URL
|
||||
// */
|
||||
// function buildPublicCdnUrl(fileName: string) {
|
||||
// return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
|
||||
// fileName,
|
||||
// )}&raw=1`;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Ambil semua PUBLIC CDN URL
|
||||
// */
|
||||
// export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
|
||||
// const items = await getDirItems();
|
||||
|
||||
// return items
|
||||
// .filter((item) => item.type === "file")
|
||||
// .map((file) => {
|
||||
// const path = `${DIR_TARGET}/${file.name}`;
|
||||
// return {
|
||||
// name: file.name,
|
||||
// path,
|
||||
// cdnUrl: buildPublicCdnUrl(file.name),
|
||||
// };
|
||||
// });
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Run langsung (optional)
|
||||
// */
|
||||
// if (import.meta.main) {
|
||||
// const data = await getAllPublicCdnUrls();
|
||||
// console.log(data);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||
|
||||
// type CdnItem = {
|
||||
// name: string;
|
||||
// path: string;
|
||||
// cdnUrl: string;
|
||||
// };
|
||||
|
||||
// type DirItem = {
|
||||
// type: "file" | "dir";
|
||||
// name: string;
|
||||
// };
|
||||
|
||||
// // ✅ PAKAI ENV YANG BENAR
|
||||
// const BASE_URL = process.env.SEAFILE_URL!;
|
||||
// const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||
// const PUBLIC_SHARE_TOKEN = process.env.SEAFILE_PUBLIC_SHARE_TOKEN!;
|
||||
|
||||
// // folder yang dishare (RELATIVE, TANPA slash depan)
|
||||
// const DIR_TARGET = "asset-web";
|
||||
|
||||
// /**
|
||||
// * Ambil list file dari repo (token dipakai SEKALI)
|
||||
// */
|
||||
// async function getDirItems(): Promise<DirItem[]> {
|
||||
// if (!BASE_URL || !REPO_ID) {
|
||||
// throw new Error("SEAFILE env not configured correctly");
|
||||
// }
|
||||
|
||||
// const token = await getValidAuthToken();
|
||||
|
||||
// const url = `${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`;
|
||||
|
||||
// const res = await fetch(url, {
|
||||
// headers: {
|
||||
// Authorization: `Token ${token}`,
|
||||
// },
|
||||
// });
|
||||
|
||||
// if (!res.ok) {
|
||||
// const text = await res.text();
|
||||
// throw new Error(`Failed get dir items: ${text}`);
|
||||
// }
|
||||
|
||||
// return res.json();
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Build PUBLIC CDN URL (DIRECTORY SHARE)
|
||||
// */
|
||||
// function buildPublicCdnUrl(fileName: string) {
|
||||
// const fullPath = `/${DIR_TARGET}/${fileName}`;
|
||||
// return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
|
||||
// fullPath,
|
||||
// )}&raw=1`;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Ambil semua PUBLIC CDN URL
|
||||
// */
|
||||
// export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
|
||||
// const items = await getDirItems();
|
||||
|
||||
// return items
|
||||
// .filter((item) => item.type === "file")
|
||||
// .map((file) => ({
|
||||
// name: file.name,
|
||||
// path: `${DIR_TARGET}/${file.name}`,
|
||||
// cdnUrl: buildPublicCdnUrl(file.name),
|
||||
// }));
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Run langsung
|
||||
// */
|
||||
// if (import.meta.main) {
|
||||
// const data = await getAllPublicCdnUrls();
|
||||
// console.log(data);
|
||||
// }
|
||||
|
||||
|
||||
import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||
type CdnItem = {
|
||||
name: string;
|
||||
path: string;
|
||||
cdnUrl: string;
|
||||
};
|
||||
type DirItem = {
|
||||
type: "file" | "dir";
|
||||
name: string;
|
||||
};
|
||||
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
|
||||
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||
// folder yang dishare (RELATIVE, tanpa slash depan)
|
||||
const DIR_TARGET = "asset-web";
|
||||
// 🔑 TOKEN DIRECTORY SHARE (/d/{token})
|
||||
const PUBLIC_SHARE_TOKEN = "3a9a9ecb5e244f4da8ae";
|
||||
/**
|
||||
* Ambil list file dari repo (butuh token sekali)
|
||||
*/
|
||||
async function getDirItems(): Promise<DirItem[]> {
|
||||
const token = await getValidAuthToken();
|
||||
const res = await fetch(
|
||||
`${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Token ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`Failed get dir items: ${text}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
/**
|
||||
* Build PUBLIC CDN URL
|
||||
*/
|
||||
function buildPublicCdnUrl(fileName: string) {
|
||||
return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
|
||||
fileName,
|
||||
)}&raw=1`;
|
||||
}
|
||||
/**
|
||||
* Ambil semua PUBLIC CDN URL
|
||||
*/
|
||||
export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
|
||||
const items = await getDirItems();
|
||||
return items
|
||||
.filter((item) => item.type === "file")
|
||||
.map((file) => {
|
||||
// const path = `${DIR_TARGET}/${file.name}`;
|
||||
const path = `/${file.name}`;
|
||||
return {
|
||||
name: file.name,
|
||||
path,
|
||||
cdnUrl: buildPublicCdnUrl(file.name),
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Run langsung (optional)
|
||||
*/
|
||||
if (import.meta.main) {
|
||||
const data = await getAllPublicCdnUrls();
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||
|
||||
type DirItem = {
|
||||
type: "file" | "dir";
|
||||
name: string;
|
||||
@@ -5,7 +7,6 @@ type DirItem = {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
const TOKEN = process.env.SEAFILE_TOKEN!;
|
||||
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||
|
||||
// ⛔ PENTING: RELATIVE PATH (tanpa slash depan)
|
||||
@@ -13,11 +14,12 @@ const DIR_TARGET = "asset-web";
|
||||
|
||||
const BASE_URL = process.env.SEAFILE_URL;
|
||||
|
||||
const headers = {
|
||||
Authorization: `Token ${TOKEN}`,
|
||||
};
|
||||
|
||||
async function getDirItems(): Promise<DirItem[]> {
|
||||
const token = await getValidAuthToken();
|
||||
const headers = {
|
||||
Authorization: `Token ${token}`,
|
||||
};
|
||||
|
||||
const res = await fetch(`${BASE_URL}/repos/${REPO_ID}/dir/?p=${DIR_TARGET}`, {
|
||||
headers,
|
||||
});
|
||||
@@ -30,6 +32,11 @@ async function getDirItems(): Promise<DirItem[]> {
|
||||
}
|
||||
|
||||
async function getDownloadUrl(filePath: string): Promise<string> {
|
||||
const token = await getValidAuthToken();
|
||||
const headers = {
|
||||
Authorization: `Token ${token}`,
|
||||
};
|
||||
|
||||
const res = await fetch(
|
||||
`${BASE_URL}/repos/${REPO_ID}/file/?p=${encodeURIComponent(filePath)}&reuse=1`,
|
||||
{ headers },
|
||||
|
||||
71
prisma/lib/get_shared_images.ts
Normal file
71
prisma/lib/get_shared_images.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
//ini code awal cari image by folder di seafile
|
||||
|
||||
|
||||
type CdnItem = {
|
||||
name: string;
|
||||
path: string;
|
||||
cdnUrl: string;
|
||||
};
|
||||
|
||||
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
|
||||
const SHARE_ID = "3325e9db2c504ebf9584";
|
||||
|
||||
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/
|
||||
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/files/?p=-M_tICRVz6ZxOfvkuHQgU-mobile.webp&raw=1
|
||||
|
||||
|
||||
/**
|
||||
* Build CDN URL langsung (tanpa API, tanpa token)
|
||||
*/
|
||||
export function buildCdnUrl(filePath: string) {
|
||||
// filePath contoh: "banner/home.jpg"
|
||||
return `${BASE_URL}/f/${SHARE_ID}/${filePath}?raw=1`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ambil daftar file dari PUBLIC SHARE (optional)
|
||||
* Tidak pakai token
|
||||
*/
|
||||
async function getPublicDirItems(path = "/"): Promise<any[]> {
|
||||
const res = await fetch(
|
||||
`${BASE_URL}/api/v2.1/share-links/${SHARE_ID}/dir/?p=${encodeURIComponent(
|
||||
path,
|
||||
)}`,
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`Failed get public dir items: ${text}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ambil semua CDN URL dari folder public share
|
||||
*/
|
||||
export async function getAllCdnUrls(
|
||||
dirPath = "/",
|
||||
): Promise<CdnItem[]> {
|
||||
const items = await getPublicDirItems(dirPath);
|
||||
|
||||
return items
|
||||
.filter((item: any) => item.type === "file")
|
||||
.map((file: any) => {
|
||||
const filePath =
|
||||
dirPath === "/"
|
||||
? file.name
|
||||
: `${dirPath.replace(/\/$/, "")}/${file.name}`;
|
||||
|
||||
return {
|
||||
name: file.name,
|
||||
path: filePath,
|
||||
cdnUrl: buildCdnUrl(filePath),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if(import.meta.main) {
|
||||
const allCdnUrls = await getAllCdnUrls();
|
||||
console.log(allCdnUrls);
|
||||
}
|
||||
33
prisma/lib/get_sharef.ts
Normal file
33
prisma/lib/get_sharef.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
|
||||
const ADMIN_TOKEN = process.env.SEAFILE_TOKEN!;
|
||||
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||
|
||||
export async function createFileShareForFolder() {
|
||||
const res = await fetch(`${BASE_URL}/api/v2.1/share-links/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Token ${ADMIN_TOKEN}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
repo_id: REPO_ID,
|
||||
path: "/asset-web", // FOLDER
|
||||
permission: "r",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
console.log("FILE SHARE LINK:", data);
|
||||
|
||||
// data.link -> https://domain/f/XXXX/
|
||||
// data.token / data.id (tergantung versi)
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await createFileShareForFolder();
|
||||
}
|
||||
@@ -1659,8 +1659,8 @@ model DesaDigital {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -1710,8 +1710,8 @@ model InfoTekno {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -1766,8 +1766,8 @@ model PengaduanMasyarakat {
|
||||
nik String
|
||||
judulPengaduan String
|
||||
lokasiKejadian String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
deskripsiPengaduan String @db.Text
|
||||
jenisPengaduan JenisPengaduan @relation(fields: [jenisPengaduanId], references: [id])
|
||||
jenisPengaduanId String
|
||||
@@ -1848,8 +1848,8 @@ model KegiatanDesa {
|
||||
tanggal DateTime
|
||||
lokasi String
|
||||
partisipan Int
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -2133,8 +2133,8 @@ model DataPerpustakaan {
|
||||
deskripsi String @db.Text
|
||||
kategori KategoriBuku @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
|
||||
1771
prisma/seed.ts
1771
prisma/seed.ts
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,37 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
import { getAllDownloadUrls } from "./lib/get_images";
|
||||
import { getAllPublicCdnUrls } from "./lib/create_file_share_folder";
|
||||
|
||||
export default async function seedAssets() {
|
||||
const images = await getAllDownloadUrls();
|
||||
|
||||
// for (const img of images) {
|
||||
// try {
|
||||
// await prisma.fileStorage.upsert({
|
||||
// where: { name: img.name },
|
||||
// create: {
|
||||
// name: img.name,
|
||||
// category: "image",
|
||||
// mimeType: "image/webp",
|
||||
// link: img.downloadUrl,
|
||||
// path: "images",
|
||||
// realName: img.name,
|
||||
// isActive: true,
|
||||
// },
|
||||
// update: {
|
||||
// link: img.downloadUrl,
|
||||
// isActive: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
// console.log(`✅ ${img.name}`);
|
||||
// } catch (err: any) {
|
||||
// console.error(`❌ ${img.name}`, err.code ?? err);
|
||||
// }
|
||||
// }
|
||||
const images = await getAllPublicCdnUrls();
|
||||
|
||||
for (const img of images) {
|
||||
try {
|
||||
await prisma.fileStorage.upsert({
|
||||
where: {
|
||||
id: img.name,
|
||||
},
|
||||
create: {
|
||||
name: img.name,
|
||||
category: "image",
|
||||
mimeType: "image/webp",
|
||||
link: img.downloadUrl,
|
||||
path: "images",
|
||||
realName: img.name,
|
||||
isActive: true,
|
||||
},
|
||||
update: {},
|
||||
// Check if the image already exists by name
|
||||
const existingImage = await prisma.fileStorage.findUnique({
|
||||
where: { name: img.name },
|
||||
});
|
||||
console.log(img.name, ": success")
|
||||
|
||||
if (!existingImage) {
|
||||
// Only create if it doesn't exist
|
||||
await prisma.fileStorage.create({
|
||||
data: {
|
||||
name: img.name,
|
||||
category: "image",
|
||||
mimeType: "image/webp",
|
||||
link: img.cdnUrl,
|
||||
path: "images",
|
||||
realName: img.name,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log(`✅ Created new image: ${img.name}`);
|
||||
} else {
|
||||
console.log(`ℹ️ Image already exists, skipping: ${img.name}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("gagal seed assets", JSON.stringify(err));
|
||||
console.log(`❌ Failed to seed asset ${img.name}:`, JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
21
public/manifest.json
Normal file
21
public/manifest.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Desa Darmasaba",
|
||||
"short_name": "Darmasaba",
|
||||
"description": "Website resmi Desa Darmasaba, Kabupaten Badung, Bali",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#1e40af",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/darmasaba-icon.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/darmasaba-icon.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -57,12 +57,24 @@ const stateProfilePPID = proxy({
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
} else throw new Error(result.message || "Gagal memuat data profile");
|
||||
} else {
|
||||
// Jika pesan adalah "Data tidak ditemukan" atau "Belum ada data profil PPID yang aktif",
|
||||
// tetap simpan sebagai error tapi tidak perlu menampilkan toast error karena ini bukan error sebenarnya
|
||||
if (result.message === "Data tidak ditemukan" || result.message === "Belum ada data profil PPID yang aktif") {
|
||||
this.error = result.message;
|
||||
return null;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal memuat data profile");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = (err as Error).message;
|
||||
this.error = msg;
|
||||
console.error("Load profile error:", msg);
|
||||
toast.error("Gagal memuat data profile");
|
||||
// Hanya tampilkan toast error jika bukan karena data tidak ditemukan
|
||||
if (msg !== "Data tidak ditemukan" && msg !== "Belum ada data profil PPID yang aktif") {
|
||||
toast.error("Gagal memuat data profile");
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
|
||||
@@ -33,6 +33,13 @@ function EditKategoriBerita() {
|
||||
name: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -72,6 +79,11 @@ function EditKategoriBerita() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Nama kategori berita wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// update global state hanya saat submit
|
||||
@@ -143,8 +155,11 @@ function EditKategoriBerita() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -22,6 +22,13 @@ function CreateKategoriBerita() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
createState.create.form.name?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: '',
|
||||
@@ -29,6 +36,11 @@ function CreateKategoriBerita() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!createState.create.form.name?.trim()) {
|
||||
toast.error('Nama kategori berita wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await createState.create.create();
|
||||
@@ -93,8 +105,11 @@ function CreateKategoriBerita() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -48,6 +48,24 @@ function EditBerita() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
formData.kategoriBeritaId !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
(file !== null || originalData.imageId !== '') && // Either a new file is selected or an existing image exists
|
||||
!isHtmlEmpty(formData.content)
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
@@ -103,6 +121,31 @@ function EditBerita() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.kategoriBeritaId) {
|
||||
toast.error('Kategori wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi singkat wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file && !originalData.imageId) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.content)) {
|
||||
toast.error('Konten wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya sekali di sini
|
||||
@@ -326,8 +369,11 @@ function EditBerita() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -32,6 +32,24 @@ export default function CreateBerita() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
beritaState.berita.create.form.judul?.trim() !== '' &&
|
||||
beritaState.berita.create.form.kategoriBeritaId !== '' &&
|
||||
!isHtmlEmpty(beritaState.berita.create.form.deskripsi) &&
|
||||
file !== null &&
|
||||
!isHtmlEmpty(beritaState.berita.create.form.content)
|
||||
);
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
beritaState.kategoriBerita.findMany.load();
|
||||
}, []);
|
||||
@@ -49,6 +67,31 @@ export default function CreateBerita() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!beritaState.berita.create.form.judul?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!beritaState.berita.create.form.kategoriBeritaId) {
|
||||
toast.error('Kategori wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(beritaState.berita.create.form.deskripsi)) {
|
||||
toast.error('Deskripsi singkat wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(beritaState.berita.create.form.content)) {
|
||||
toast.error('Konten wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
@@ -250,8 +293,11 @@ export default function CreateBerita() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -45,6 +45,22 @@ function EditFoto() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
(file !== null || originalData.imagesId !== '') // Either a new file is selected or an existing image exists
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
@@ -94,6 +110,21 @@ function EditFoto() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file && !originalData.imagesId) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya sekali di sini
|
||||
@@ -285,8 +316,11 @@ function EditFoto() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -30,6 +30,22 @@ function CreateFoto() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
FotoState.create.form.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(FotoState.create.form.deskripsi) &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
FotoState.create.form = {
|
||||
name: '',
|
||||
@@ -41,6 +57,21 @@ function CreateFoto() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!FotoState.create.form.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(FotoState.create.form.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
@@ -210,8 +241,11 @@ function CreateFoto() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -28,6 +28,24 @@ function EditVideo() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.linkVideo?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
embedLink !== null // Make sure the embed link is valid
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
@@ -86,6 +104,21 @@ function EditVideo() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.linkVideo?.trim()) {
|
||||
toast.error('Link YouTube wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||
@@ -218,8 +251,11 @@ function EditVideo() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -28,6 +28,23 @@ function CreateVideo() {
|
||||
const embedLink = convertYoutubeUrlToEmbed(link);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
videoState.create.form.name?.trim() !== '' &&
|
||||
link.trim() !== '' &&
|
||||
!isHtmlEmpty(videoState.create.form.deskripsi) &&
|
||||
embedLink !== null // Make sure the embed link is valid
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
videoState.create.form = {
|
||||
name: '',
|
||||
@@ -38,6 +55,26 @@ function CreateVideo() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!videoState.create.form.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!link.trim()) {
|
||||
toast.error('Link YouTube wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(videoState.create.form.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!embedLink) {
|
||||
toast.error('Link YouTube tidak valid. Pastikan formatnya benar.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!embedLink) {
|
||||
@@ -168,8 +205,11 @@ function CreateVideo() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -35,6 +35,21 @@ function EditPelayananPendudukNonPermanent() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
@@ -86,6 +101,16 @@ function EditPelayananPendudukNonPermanent() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!statePendudukNonPermanent.findById.data) return;
|
||||
@@ -173,8 +198,11 @@ function EditPelayananPendudukNonPermanent() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -36,6 +36,22 @@ function EditPelayananPerizinanBerusaha() {
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -102,6 +118,21 @@ function EditPelayananPerizinanBerusaha() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.link?.trim()) {
|
||||
toast.error('Link wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await state.update.update(formData);
|
||||
@@ -192,8 +223,11 @@ function EditPelayananPerizinanBerusaha() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -149,6 +149,22 @@ function EditSuratKeterangan() {
|
||||
const [previewImage2, setPreviewImage2] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
(previewImage !== null || originalData.imageId !== '') // Either a new file is selected or an existing image exists
|
||||
);
|
||||
};
|
||||
|
||||
// 🧭 Load Initial Data
|
||||
useEffect(() => {
|
||||
const loadSurat = async () => {
|
||||
@@ -209,6 +225,21 @@ function EditSuratKeterangan() {
|
||||
|
||||
// 💾 Submit Handler
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Nama surat keterangan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Konten wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!previewImage && !originalData.imageId) {
|
||||
toast.error('Gambar konten pelayanan wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
|
||||
@@ -251,7 +282,7 @@ function EditSuratKeterangan() {
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [formData, file, file2, router]);
|
||||
}, [formData, file, file2, router, previewImage, originalData.imageId]);
|
||||
|
||||
// 📝 Form Field Handlers
|
||||
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -324,10 +355,10 @@ function EditSuratKeterangan() {
|
||||
|
||||
{/* Action Buttons */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
onClick={handleResetForm}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
@@ -336,9 +367,11 @@ function EditSuratKeterangan() {
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
disabled={isSubmitting}
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79,172,254,0.4)',
|
||||
}}
|
||||
|
||||
@@ -31,6 +31,22 @@ function CreateSuratKeterangan() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateSurat.create.form.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateSurat.create.form.deskripsi) &&
|
||||
previewImage !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateSurat.create.form = {
|
||||
name: '',
|
||||
@@ -43,8 +59,19 @@ function CreateSuratKeterangan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!stateSurat.create.form.name?.trim()) {
|
||||
toast.error('Nama surat keterangan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(stateSurat.create.form.deskripsi)) {
|
||||
toast.error('Konten wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!previewImage) {
|
||||
return toast.warn('Pilih file gambar utama terlebih dahulu');
|
||||
toast.error('Gambar konten pelayanan wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -284,8 +311,11 @@ function CreateSuratKeterangan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -24,6 +24,15 @@ function EditPelayananTelunjukSakti() {
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.deskripsi?.trim() !== '' &&
|
||||
formData.link?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
@@ -84,6 +93,21 @@ function EditPelayananTelunjukSakti() {
|
||||
|
||||
// Submit: update global state hanya saat simpan
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Nama pelayanan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.deskripsi?.trim()) {
|
||||
toast.error('Judul link wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.link?.trim()) {
|
||||
toast.error('Link wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateTelunjukDesa.edit.form = {
|
||||
@@ -165,8 +189,11 @@ function EditPelayananTelunjukSakti() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -23,6 +23,15 @@ function CreatePelayananTelunjukDesa() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateTelunjukDesa.create.form.name?.trim() !== '' &&
|
||||
stateTelunjukDesa.create.form.deskripsi?.trim() !== '' &&
|
||||
stateTelunjukDesa.create.form.link?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateTelunjukDesa.create.form = {
|
||||
name: '',
|
||||
@@ -32,6 +41,21 @@ function CreatePelayananTelunjukDesa() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!stateTelunjukDesa.create.form.name?.trim()) {
|
||||
toast.error('Nama pelayanan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stateTelunjukDesa.create.form.deskripsi?.trim()) {
|
||||
toast.error('Judul link wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stateTelunjukDesa.create.form.link?.trim()) {
|
||||
toast.error('Link wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await stateTelunjukDesa.create.create();
|
||||
@@ -118,8 +142,11 @@ function CreatePelayananTelunjukDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -33,6 +33,23 @@ function EditPenghargaan() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.juara?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
(file !== null || originalData.imageId !== '') // Either a new file is selected or an existing image exists
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: "",
|
||||
juara: "",
|
||||
@@ -100,6 +117,26 @@ function EditPenghargaan() {
|
||||
|
||||
// Submit
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Nama penghargaan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.juara?.trim()) {
|
||||
toast.error('Juara wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file && !originalData.imageId) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Sync ke global state saat submit
|
||||
@@ -281,8 +318,11 @@ function EditPenghargaan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -30,6 +30,23 @@ function CreatePenghargaan() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
statePenghargaan.create.form.name?.trim() !== '' &&
|
||||
statePenghargaan.create.form.juara?.trim() !== '' &&
|
||||
!isHtmlEmpty(statePenghargaan.create.form.deskripsi) &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
statePenghargaan.create.form = {
|
||||
name: '',
|
||||
@@ -42,6 +59,26 @@ function CreatePenghargaan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!statePenghargaan.create.form.name?.trim()) {
|
||||
toast.error('Nama penghargaan wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!statePenghargaan.create.form.juara?.trim()) {
|
||||
toast.error('Juara wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(statePenghargaan.create.form.deskripsi)) {
|
||||
toast.error('Deskripsi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
toast.error('Gambar wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
@@ -201,8 +238,11 @@ function CreatePenghargaan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,13 @@ function EditKategoriPengumuman() {
|
||||
const [formData, setFormData] = useState({ name: '' });
|
||||
const [originalData, setOriginalData] = useState({ name: '' });
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data awal sekali aja
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
@@ -56,6 +63,11 @@ function EditKategoriPengumuman() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name?.trim()) {
|
||||
toast.error('Nama kategori pengumuman wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya di sini
|
||||
@@ -134,8 +146,11 @@ function EditKategoriPengumuman() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -22,6 +22,13 @@ function CreateKategoriPengumuman() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
createState.create.form.name?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: '',
|
||||
@@ -29,6 +36,11 @@ function CreateKategoriPengumuman() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!createState.create.form.name?.trim()) {
|
||||
toast.error('Nama kategori pengumuman wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
@@ -92,8 +104,11 @@ function CreateKategoriPengumuman() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -36,6 +36,23 @@ function EditPengumuman() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
formData.deskripsi?.trim() !== '' &&
|
||||
formData.categoryPengumumanId !== '' &&
|
||||
!isHtmlEmpty(formData.content)
|
||||
);
|
||||
};
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
@@ -81,6 +98,26 @@ function EditPengumuman() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.deskripsi?.trim()) {
|
||||
toast.error('Deskripsi singkat wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.categoryPengumumanId) {
|
||||
toast.error('Kategori wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(formData.content)) {
|
||||
toast.error('Konten lengkap wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// update global state hanya sekali pas submit
|
||||
@@ -197,8 +234,11 @@ function EditPengumuman() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -27,11 +27,48 @@ function CreatePengumuman() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
pengumumanState.pengumuman.create.form.judul?.trim() !== '' &&
|
||||
pengumumanState.pengumuman.create.form.categoryPengumumanId !== '' &&
|
||||
pengumumanState.pengumuman.create.form.deskripsi?.trim() !== '' &&
|
||||
!isHtmlEmpty(pengumumanState.pengumuman.create.form.content)
|
||||
);
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
pengumumanState.category.findMany.load();
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!pengumumanState.pengumuman.create.form.judul?.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pengumumanState.pengumuman.create.form.categoryPengumumanId) {
|
||||
toast.error('Kategori wajib dipilih');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pengumumanState.pengumuman.create.form.deskripsi?.trim()) {
|
||||
toast.error('Deskripsi singkat wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHtmlEmpty(pengumumanState.pengumuman.create.form.content)) {
|
||||
toast.error('Konten lengkap wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await pengumumanState.pengumuman.create.create();
|
||||
@@ -150,8 +187,11 @@ function CreatePengumuman() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -33,6 +33,13 @@ function EditKategoriPotensi() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.nama?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data dari backend -> isi ke formData lokal
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
@@ -73,6 +80,11 @@ function EditKategoriPotensi() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.nama?.trim()) {
|
||||
toast.error('Nama kategori potensi wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya pas submit
|
||||
@@ -141,8 +153,11 @@ function EditKategoriPotensi() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user