generated from corrad-software/corrad-af-2024
Added Settings, Updated Schema & Documentation
This commit is contained in:
parent
1c0afe2b8a
commit
b157b8b47d
204
CHANGELOG.md
204
CHANGELOG.md
@ -1,204 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the Electronic Document Management System (EDMS) project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.0.0] - 2024-01-15
|
||||
|
||||
### 🎨 **Major Design System Overhaul**
|
||||
|
||||
#### Added
|
||||
- **New Standardized Components**
|
||||
- `RsInput.vue`: Comprehensive input component with validation, sizing, and error states
|
||||
- `RsSelect.vue`: Dropdown/select component with single/multiple selection support
|
||||
- `RsTextarea.vue`: Textarea component with configurable resize and validation
|
||||
- Design system documentation page at `/dms/design-system`
|
||||
- Interactive component playground with live examples
|
||||
- Component usage guidelines and best practices
|
||||
|
||||
- **Enhanced Design System Features**
|
||||
- Consistent prop structure across all Rs components (`modelValue`, `label`, `error`, `disabled`, `size`)
|
||||
- Universal dark mode support via CSS variables
|
||||
- Accessibility features with proper ARIA attributes
|
||||
- Standardized sizing system (sm, md, lg)
|
||||
- Error state handling with validation messaging
|
||||
- Real-time validation and form state management
|
||||
|
||||
#### Changed
|
||||
- **Settings Architecture Simplification**
|
||||
- Reduced from 10 to 5 core settings categories:
|
||||
- 🔐 User & Access Management
|
||||
- 📁 Document & Folder Settings
|
||||
- 📝 Metadata & Tagging
|
||||
- 📤 Upload & Storage Settings
|
||||
- 📅 System Settings
|
||||
- Removed categories: Search & Indexing, Integration Settings, Audit & Compliance, Workflow & Automation, UI & Branding
|
||||
- Streamlined settings structure for better usability and maintenance
|
||||
|
||||
- **Component Standardization**
|
||||
- `DMSAccessRequestDialog.vue`: Replaced manual button styling with `RsButton` components
|
||||
- Settings page: Updated key form inputs to use `RsInput`, `RsSelect`, `RsTextarea`
|
||||
- Standardized modal footer patterns across all dialogs
|
||||
- Consistent form validation and error handling
|
||||
|
||||
- **Enhanced Component Registration**
|
||||
- Added new Rs components to global component registry
|
||||
- Updated export system in `pages/devtool/code-playground/index.js`
|
||||
- Improved component discovery and usage
|
||||
|
||||
#### Improved
|
||||
- **Form Consistency**
|
||||
- Unified form styling across all components
|
||||
- Consistent error handling and validation states
|
||||
- Standardized label and placeholder patterns
|
||||
- Improved accessibility with proper form associations
|
||||
|
||||
- **User Experience**
|
||||
- Better visual consistency across the application
|
||||
- Improved dark mode support and theme switching
|
||||
- Enhanced form validation with real-time feedback
|
||||
- Standardized interaction patterns
|
||||
|
||||
### 📖 **Documentation Updates**
|
||||
|
||||
#### Added
|
||||
- **Comprehensive Design System Documentation**
|
||||
- Interactive component playground at `/dms/design-system`
|
||||
- Component API documentation with props and examples
|
||||
- Usage guidelines and best practices
|
||||
- Color system and typography scale documentation
|
||||
- Accessibility standards and compliance information
|
||||
|
||||
- **Enhanced Technical Guide**
|
||||
- Complete design system section with component specifications
|
||||
- Form component usage examples and API reference
|
||||
- Color system documentation with CSS variable reference
|
||||
- Typography and spacing scale definitions
|
||||
- Integration patterns and best practices
|
||||
|
||||
#### Updated
|
||||
- **User Guide Enhancements**
|
||||
- Added comprehensive Settings Administration section
|
||||
- Documented simplified 5-category settings structure
|
||||
- Detailed explanation of each settings category
|
||||
- Best practices for settings management
|
||||
- Troubleshooting guide for settings issues
|
||||
|
||||
- **README Overhaul**
|
||||
- Complete project overview with features and technology stack
|
||||
- Design system highlights and component examples
|
||||
- Comprehensive setup and installation instructions
|
||||
- Development guidelines and contribution standards
|
||||
- Security features and support information
|
||||
|
||||
### 🔧 **Technical Improvements**
|
||||
|
||||
#### Enhanced
|
||||
- **CSS Architecture**
|
||||
- Improved CSS variable system for theming
|
||||
- Standardized component styling patterns
|
||||
- Better organization of style files
|
||||
- Enhanced dark mode support
|
||||
|
||||
- **Form Handling**
|
||||
- Consistent v-model patterns across components
|
||||
- Improved validation and error state management
|
||||
- Better form component composition
|
||||
- Enhanced accessibility features
|
||||
|
||||
- **State Management**
|
||||
- Improved settings state management
|
||||
- Better validation and error handling
|
||||
- Enhanced form state synchronization
|
||||
|
||||
### 🚨 **Breaking Changes**
|
||||
- Settings API structure changed due to category reduction
|
||||
- Some component prop names standardized (may affect custom implementations)
|
||||
- Manual form styling replaced with Rs components (update required for custom forms)
|
||||
|
||||
### 💻 **Development Experience**
|
||||
|
||||
#### Improved
|
||||
- **Component Development**
|
||||
- Standardized component patterns for easier development
|
||||
- Better TypeScript support for component props
|
||||
- Improved component documentation and examples
|
||||
- Enhanced development guidelines
|
||||
|
||||
- **Design Consistency**
|
||||
- Clear design system guidelines
|
||||
- Standardized component usage patterns
|
||||
- Better visual consistency across the application
|
||||
- Improved maintainability
|
||||
|
||||
#### Added
|
||||
- **Development Tools**
|
||||
- Interactive design system playground
|
||||
- Component examples and usage guidelines
|
||||
- Development best practices documentation
|
||||
- Contributing guidelines for design system
|
||||
|
||||
### 📱 **User Interface**
|
||||
|
||||
#### Enhanced
|
||||
- **Visual Consistency**
|
||||
- Unified button styles and interactions
|
||||
- Consistent form field appearance
|
||||
- Standardized modal and dialog layouts
|
||||
- Improved color scheme and theming
|
||||
|
||||
- **Accessibility**
|
||||
- Better keyboard navigation support
|
||||
- Improved screen reader compatibility
|
||||
- Consistent focus states and indicators
|
||||
- WCAG 2.1 compliance improvements
|
||||
|
||||
- **Responsive Design**
|
||||
- Better mobile form interactions
|
||||
- Improved tablet layout handling
|
||||
- Consistent breakpoint management
|
||||
- Enhanced touch interaction support
|
||||
|
||||
### 🔄 **Migration Guide**
|
||||
|
||||
#### For Developers
|
||||
1. **Update Form Components**: Replace manual form styling with Rs components
|
||||
2. **Settings Integration**: Update any settings-related code for new 5-category structure
|
||||
3. **Component Usage**: Follow new Rs component patterns for consistency
|
||||
4. **Styling Updates**: Remove manual Tailwind classes in favor of Rs components
|
||||
|
||||
#### For Users
|
||||
1. **Settings Location**: Settings now organized in 5 streamlined categories
|
||||
2. **Interface Changes**: Improved form interactions and validation
|
||||
3. **New Features**: Access to design system documentation and examples
|
||||
|
||||
### 🎯 **Future Roadmap**
|
||||
- Additional Rs components (date picker, multi-select, autocomplete)
|
||||
- Enhanced animation system for micro-interactions
|
||||
- Expanded accessibility features and testing
|
||||
- Advanced form validation and error handling
|
||||
- Component testing and documentation automation
|
||||
|
||||
---
|
||||
|
||||
## [1.0.0] - 2023-12-01
|
||||
|
||||
### Initial Release
|
||||
- Electronic Document Management System with hierarchical organization
|
||||
- User authentication and role-based access control
|
||||
- Document upload, preview, and management capabilities
|
||||
- Basic settings and configuration system
|
||||
- Initial UI components and styling
|
||||
|
||||
---
|
||||
|
||||
**Legend:**
|
||||
- 🎨 Design System
|
||||
- 📖 Documentation
|
||||
- 🔧 Technical
|
||||
- 🚨 Breaking Changes
|
||||
- 💻 Development Experience
|
||||
- 📱 User Interface
|
||||
- 🔄 Migration
|
||||
- 🎯 Roadmap
|
253
README.md
253
README.md
@ -1,256 +1,45 @@
|
||||
# Electronic Document Management System (EDMS)
|
||||
# Nuxt 3 Minimal Starter
|
||||
|
||||
A modern, web-based document management system built with Nuxt.js 3 and Vue.js 3, featuring a comprehensive design system and hierarchical document organization.
|
||||
Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more.
|
||||
|
||||
## 🚀 Features
|
||||
## Setup
|
||||
|
||||
### Core Functionality
|
||||
- **Hierarchical Organization**: Cabinet → Drawer → Folder → Subfolder structure
|
||||
- **Advanced Document Management**: Upload, version control, metadata management
|
||||
- **Role-Based Access Control**: Granular permissions with access request workflows
|
||||
- **Multi-format Document Viewer**: Built-in viewer for PDF, images, Office documents
|
||||
- **Advanced Search**: Full-text search with metadata and tag filtering
|
||||
- **Responsive Design**: Works seamlessly across desktop, tablet, and mobile
|
||||
Make sure to install the dependencies:
|
||||
|
||||
### Design System
|
||||
- **Standardized Components**: Complete "Rs" component library (RsButton, RsInput, RsSelect, etc.)
|
||||
- **Consistent Styling**: Unified design patterns with dark/light mode support
|
||||
- **Accessibility**: WCAG-compliant components with proper ARIA attributes
|
||||
- **Interactive Documentation**: Design system playground at `/dms/design-system`
|
||||
|
||||
### Administration
|
||||
- **Streamlined Settings**: 5 core configuration categories
|
||||
- 🔐 User & Access Management
|
||||
- 📁 Document & Folder Settings
|
||||
- 📝 Metadata & Tagging
|
||||
- 📤 Upload & Storage Settings
|
||||
- 📅 System Settings
|
||||
- **Import/Export**: Configuration backup and transfer capabilities
|
||||
- **Real-time Validation**: Form validation with dependency checking
|
||||
|
||||
## 🛠️ Technology Stack
|
||||
|
||||
### Frontend
|
||||
- **Nuxt.js 3**: Universal Vue.js framework with SSR/SPA support
|
||||
- **Vue.js 3**: Progressive framework with Composition API
|
||||
- **TailwindCSS**: Utility-first CSS framework with custom component system
|
||||
- **Pinia**: Modern state management with persistence
|
||||
- **FormKit**: Advanced form handling with custom theming
|
||||
|
||||
### Backend
|
||||
- **Prisma ORM**: Type-safe database client with migrations
|
||||
- **MySQL/PostgreSQL**: Relational database with comprehensive schema
|
||||
- **File System Integration**: Secure file storage and management
|
||||
- **JWT Authentication**: Token-based authentication with RBAC
|
||||
|
||||
### Development Tools
|
||||
- **TypeScript**: Type safety and enhanced development experience
|
||||
- **ESLint**: Code linting with Vue.js specific rules
|
||||
- **Vite**: Lightning-fast build tool and HMR
|
||||
|
||||
## 📋 Setup
|
||||
|
||||
### Requirements
|
||||
- Node.js 18+ and npm/yarn/pnpm
|
||||
- MySQL 8+ or PostgreSQL 13+
|
||||
- Modern web browser with JavaScript enabled
|
||||
|
||||
### Installation
|
||||
|
||||
1. **Clone Repository**
|
||||
```bash
|
||||
git clone https://github.com/your-repo/edms.git
|
||||
cd edms
|
||||
```
|
||||
|
||||
2. **Install Dependencies**
|
||||
```bash
|
||||
# Using npm
|
||||
npm install
|
||||
|
||||
# Using yarn
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# Using pnpm
|
||||
pnpm install
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install --shamefully-hoist
|
||||
```
|
||||
|
||||
3. **Environment Configuration**
|
||||
Create `.env` file:
|
||||
```env
|
||||
# Database
|
||||
DATABASE_URL="mysql://username:password@localhost:3306/edms_db"
|
||||
## Development Server
|
||||
|
||||
# Authentication
|
||||
JWT_SECRET="your-jwt-secret-key-min-256-bits"
|
||||
SESSION_SECRET="your-session-secret-key"
|
||||
Start the development server on http://localhost:3000
|
||||
|
||||
# File Storage
|
||||
UPLOAD_PATH="/var/uploads/edms"
|
||||
MAX_FILE_SIZE="104857600" # 100MB
|
||||
ALLOWED_FILE_TYPES="pdf,doc,docx,xls,xlsx,ppt,pptx,txt,jpg,jpeg,png"
|
||||
|
||||
# Application
|
||||
NUXT_SECRET_KEY="your-nuxt-app-secret"
|
||||
BASE_URL="http://localhost:3000"
|
||||
```
|
||||
|
||||
4. **Database Setup**
|
||||
```bash
|
||||
# Generate Prisma client
|
||||
npx prisma generate
|
||||
|
||||
# Run migrations
|
||||
npx prisma db push
|
||||
|
||||
# Seed data (optional)
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
5. **Development Server**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Access the application at `http://localhost:3000`
|
||||
## Production
|
||||
|
||||
## 🎨 Design System
|
||||
|
||||
### Component Library
|
||||
The EDMS uses a standardized component library with the "Rs" prefix:
|
||||
|
||||
#### Form Components
|
||||
- **RsInput**: Standardized input fields with validation
|
||||
- **RsSelect**: Dropdown/select with options support
|
||||
- **RsTextarea**: Multi-line text input with resize controls
|
||||
- **RsButton**: Buttons with multiple variants and sizes
|
||||
|
||||
#### UI Components
|
||||
- **RsCard**: Container component with header/body/footer
|
||||
- **RsModal**: Modal dialogs with standardized structure
|
||||
- **RsTable**: Data tables with sorting and filtering
|
||||
- **RsDropdown**: Dropdown menus and navigation
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```vue
|
||||
<!-- Input with validation -->
|
||||
<rs-input
|
||||
v-model="email"
|
||||
label="Email Address"
|
||||
type="email"
|
||||
:required="true"
|
||||
:error="emailError"
|
||||
/>
|
||||
|
||||
<!-- Button with variants -->
|
||||
<rs-button variant="primary" size="md" @click="save">
|
||||
Save Changes
|
||||
</rs-button>
|
||||
|
||||
<!-- Modal with standardized structure -->
|
||||
<rs-modal :visible="showModal" @close="closeModal">
|
||||
<template #header>Modal Title</template>
|
||||
<template #body>Modal content</template>
|
||||
<template #footer>
|
||||
<rs-button variant="secondary" @click="closeModal">Cancel</rs-button>
|
||||
<rs-button variant="primary" @click="confirm">Confirm</rs-button>
|
||||
</template>
|
||||
</rs-modal>
|
||||
```
|
||||
|
||||
### Design Principles
|
||||
- **🎯 Consistency**: Unified patterns across all components
|
||||
- **🔧 Modularity**: Reusable and composable components
|
||||
- **🌙 Dark Mode**: Universal theme support
|
||||
- **📱 Responsive**: Mobile-first approach
|
||||
- **♿ Accessibility**: WCAG-compliant with keyboard navigation
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
### Available Guides
|
||||
- **[Technical Guide](docs/Technical_Guide.md)**: Comprehensive technical documentation
|
||||
- **[User Guide](docs/User_Guide.md)**: End-user instructions and features
|
||||
- **[Site Settings](docs/SITE_SETTINGS.md)**: Configuration and customization guide
|
||||
|
||||
### Interactive Documentation
|
||||
- **Design System**: Visit `/dms/design-system` for component examples
|
||||
- **API Documentation**: Available in development mode
|
||||
- **Component Playground**: Test components with live examples
|
||||
|
||||
## 🔧 Development
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
edms/
|
||||
├── components/ # Vue components
|
||||
│ ├── dms/ # DMS-specific components
|
||||
│ │ ├── dialogs/ # Modal dialogs
|
||||
│ │ ├── explorer/ # Document browser
|
||||
│ │ ├── search/ # Search functionality
|
||||
│ │ └── viewers/ # Document preview
|
||||
│ └── Rs*.vue # Design system components
|
||||
├── pages/ # File-based routing
|
||||
│ ├── dms/ # DMS pages
|
||||
│ └── devtool/ # Admin tools
|
||||
├── stores/ # Pinia state management
|
||||
├── server/ # API routes and middleware
|
||||
├── prisma/ # Database schema and migrations
|
||||
├── assets/ # Stylesheets and assets
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
### Building for Production
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# Build application
|
||||
npm run build
|
||||
|
||||
# Preview production build
|
||||
npm run preview
|
||||
|
||||
# Generate static site (if applicable)
|
||||
npm run generate
|
||||
```
|
||||
|
||||
## 🔐 Security Features
|
||||
Locally preview production build:
|
||||
|
||||
- **Role-Based Access Control**: Granular permission system
|
||||
- **Document-level Security**: Individual document access controls
|
||||
- **Access Request Workflow**: Approval system for restricted documents
|
||||
- **Audit Trail**: Comprehensive activity logging
|
||||
- **File Type Validation**: Security through file type restrictions
|
||||
- **Session Management**: Secure token-based authentication
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.
|
||||
# corradAF
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Follow the design system guidelines
|
||||
4. Use standardized components (Rs library)
|
||||
5. Add tests for new functionality
|
||||
6. Commit changes (`git commit -m 'Add amazing feature'`)
|
||||
7. Push to branch (`git push origin feature/amazing-feature`)
|
||||
8. Open a Pull Request
|
||||
|
||||
### Development Guidelines
|
||||
- Use Rs components instead of custom styling
|
||||
- Follow semantic variant naming (primary, secondary, danger)
|
||||
- Test in both light and dark modes
|
||||
- Maintain accessibility standards
|
||||
- Document new features in user/technical guides
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is part of the corradAF base project. See the license file for details.
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
- **Technical Issues**: Check the [Technical Guide](docs/Technical_Guide.md)
|
||||
- **User Questions**: Refer to the [User Guide](docs/User_Guide.md)
|
||||
- **Component Usage**: Visit `/dms/design-system` for examples
|
||||
- **Bug Reports**: Create an issue with detailed reproduction steps
|
||||
|
||||
---
|
||||
|
||||
Built with ❤️ using Nuxt.js 3, Vue.js 3, and modern web technologies.
|
||||
This is the base project for corradAF.
|
||||
|
@ -1,115 +0,0 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'md'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const updateValue = (event) => {
|
||||
emit('update:modelValue', event.target.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rs-input-wrapper">
|
||||
<label v-if="label" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ label }}
|
||||
<span v-if="required" class="text-red-500">*</span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
:value="modelValue"
|
||||
@input="updateValue"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
class="rs-input"
|
||||
:class="{
|
||||
'rs-input-sm': size === 'sm',
|
||||
'rs-input-md': size === 'md',
|
||||
'rs-input-lg': size === 'lg',
|
||||
'rs-input-error': error,
|
||||
'rs-input-disabled': disabled
|
||||
}"
|
||||
/>
|
||||
|
||||
<div v-if="error" class="rs-input-error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.rs-input-wrapper {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.rs-input {
|
||||
@apply w-full px-3 py-2 border rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors;
|
||||
border-color: rgb(var(--fk-border-color));
|
||||
}
|
||||
|
||||
.rs-input:hover {
|
||||
@apply border-gray-400 dark:border-gray-500;
|
||||
}
|
||||
|
||||
.rs-input:focus {
|
||||
@apply outline-none ring-2 ring-blue-500 border-blue-500;
|
||||
}
|
||||
|
||||
.rs-input-sm {
|
||||
@apply px-2 py-1 text-sm;
|
||||
}
|
||||
|
||||
.rs-input-md {
|
||||
@apply px-3 py-2 text-sm;
|
||||
}
|
||||
|
||||
.rs-input-lg {
|
||||
@apply px-4 py-3 text-base;
|
||||
}
|
||||
|
||||
.rs-input-error {
|
||||
@apply border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500;
|
||||
}
|
||||
|
||||
.rs-input-disabled {
|
||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed;
|
||||
}
|
||||
|
||||
.rs-input-error-message {
|
||||
@apply text-sm text-red-500 mt-1;
|
||||
}
|
||||
</style>
|
@ -1,143 +0,0 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number, Array],
|
||||
default: ''
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Select an option'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'md'
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const updateValue = (event) => {
|
||||
emit('update:modelValue', event.target.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rs-select-wrapper">
|
||||
<label v-if="label" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ label }}
|
||||
<span v-if="required" class="text-red-500">*</span>
|
||||
</label>
|
||||
|
||||
<select
|
||||
:value="modelValue"
|
||||
@change="updateValue"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:multiple="multiple"
|
||||
class="rs-select"
|
||||
:class="{
|
||||
'rs-select-sm': size === 'sm',
|
||||
'rs-select-md': size === 'md',
|
||||
'rs-select-lg': size === 'lg',
|
||||
'rs-select-error': error,
|
||||
'rs-select-disabled': disabled
|
||||
}"
|
||||
>
|
||||
<option v-if="!multiple && placeholder" value="" disabled>
|
||||
{{ placeholder }}
|
||||
</option>
|
||||
<option
|
||||
v-for="option in options"
|
||||
:key="option.value || option"
|
||||
:value="option.value || option"
|
||||
>
|
||||
{{ option.label || option }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<div v-if="error" class="rs-select-error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.rs-select-wrapper {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.rs-select {
|
||||
@apply w-full px-3 py-2 border rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors appearance-none;
|
||||
border-color: rgb(var(--fk-border-color));
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5em 1.5em;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.rs-select:hover {
|
||||
@apply border-gray-400 dark:border-gray-500;
|
||||
}
|
||||
|
||||
.rs-select:focus {
|
||||
@apply outline-none ring-2 ring-blue-500 border-blue-500;
|
||||
}
|
||||
|
||||
.rs-select-sm {
|
||||
@apply px-2 py-1 text-sm;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.rs-select-md {
|
||||
@apply px-3 py-2 text-sm;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.rs-select-lg {
|
||||
@apply px-4 py-3 text-base;
|
||||
padding-right: 3rem;
|
||||
}
|
||||
|
||||
.rs-select-error {
|
||||
@apply border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500;
|
||||
}
|
||||
|
||||
.rs-select-disabled {
|
||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed;
|
||||
}
|
||||
|
||||
.rs-select-error-message {
|
||||
@apply text-sm text-red-500 mt-1;
|
||||
}
|
||||
|
||||
.rs-select[multiple] {
|
||||
background-image: none;
|
||||
padding-right: 0.75rem;
|
||||
min-height: 6rem;
|
||||
}
|
||||
</style>
|
@ -1,105 +0,0 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
resize: {
|
||||
type: String,
|
||||
default: 'vertical' // none, both, horizontal, vertical
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const updateValue = (event) => {
|
||||
emit('update:modelValue', event.target.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rs-textarea-wrapper">
|
||||
<label v-if="label" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ label }}
|
||||
<span v-if="required" class="text-red-500">*</span>
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
:value="modelValue"
|
||||
@input="updateValue"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:rows="rows"
|
||||
class="rs-textarea"
|
||||
:class="{
|
||||
'rs-textarea-error': error,
|
||||
'rs-textarea-disabled': disabled,
|
||||
'resize-none': resize === 'none',
|
||||
'resize-both': resize === 'both',
|
||||
'resize-x': resize === 'horizontal',
|
||||
'resize-y': resize === 'vertical'
|
||||
}"
|
||||
></textarea>
|
||||
|
||||
<div v-if="error" class="rs-textarea-error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.rs-textarea-wrapper {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.rs-textarea {
|
||||
@apply w-full px-3 py-2 border rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors;
|
||||
border-color: rgb(var(--fk-border-color));
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.rs-textarea:hover {
|
||||
@apply border-gray-400 dark:border-gray-500;
|
||||
}
|
||||
|
||||
.rs-textarea:focus {
|
||||
@apply outline-none ring-2 ring-blue-500 border-blue-500;
|
||||
}
|
||||
|
||||
.rs-textarea-error {
|
||||
@apply border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500;
|
||||
}
|
||||
|
||||
.rs-textarea-disabled {
|
||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed;
|
||||
}
|
||||
|
||||
.rs-textarea-error-message {
|
||||
@apply text-sm text-red-500 mt-1;
|
||||
}
|
||||
</style>
|
@ -175,24 +175,29 @@ const submitRequest = async () => {
|
||||
|
||||
<!-- Access Duration Section -->
|
||||
<div class="mb-6">
|
||||
<rs-select
|
||||
<label class="block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">Access Duration</label>
|
||||
<select
|
||||
v-model="accessDuration"
|
||||
:options="durationOptions"
|
||||
label="Access Duration"
|
||||
:required="false"
|
||||
/>
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option v-for="duration in durationOptions" :key="duration" :value="duration">
|
||||
{{ duration }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Justification Section -->
|
||||
<div class="mb-6">
|
||||
<rs-textarea
|
||||
<label class="block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">
|
||||
Justification
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
v-model="justification"
|
||||
label="Justification"
|
||||
rows="4"
|
||||
placeholder="Please explain why you need access to this document..."
|
||||
:rows="4"
|
||||
:required="true"
|
||||
resize="none"
|
||||
/>
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Footer Note -->
|
||||
@ -207,7 +212,8 @@ const submitRequest = async () => {
|
||||
<rs-button
|
||||
@click="closeDialog"
|
||||
:disabled="isSubmitting"
|
||||
variant="secondary"
|
||||
variant="secondary-outline"
|
||||
size="sm"
|
||||
>
|
||||
Cancel
|
||||
</rs-button>
|
||||
@ -215,7 +221,7 @@ const submitRequest = async () => {
|
||||
@click="submitRequest"
|
||||
:disabled="isSubmitting"
|
||||
variant="primary"
|
||||
class="flex items-center"
|
||||
size="sm"
|
||||
>
|
||||
<svg v-if="isSubmitting" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
|
@ -275,9 +275,9 @@ const getFieldComponent = (fieldType) => {
|
||||
<div v-else>
|
||||
<p class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">
|
||||
Drag and drop files here, or
|
||||
<button @click="openFileDialog" class="text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
<rs-button @click="openFileDialog" variant="primary-text" size="sm">
|
||||
browse
|
||||
</button>
|
||||
</rs-button>
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Supported: {{ dmsStore.systemSettings.upload.allowedFileTypes.join(', ').toUpperCase() }}
|
||||
@ -310,12 +310,13 @@ const getFieldComponent = (fieldType) => {
|
||||
<p class="text-sm text-gray-500">{{ formatFileSize(file.size) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="removeFile(index)"
|
||||
class="text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">
|
||||
<rs-button @click="removeFile(index)"
|
||||
variant="danger-text"
|
||||
size="sm">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</rs-button>
|
||||
</div>
|
||||
|
||||
<!-- Validation Errors -->
|
||||
|
346
composables/useDmsSettings.js
Normal file
346
composables/useDmsSettings.js
Normal file
@ -0,0 +1,346 @@
|
||||
export const useDmsSettings = () => {
|
||||
// Global DMS settings state
|
||||
const dmsSettings = useState('dmsSettings', () => ({
|
||||
// User & Access Management
|
||||
access: {
|
||||
userRoles: ['Admin', 'Editor', 'Viewer', 'Uploader'],
|
||||
rbacEnabled: true,
|
||||
userGroups: ['HR Department', 'Finance', 'IT', 'Legal'],
|
||||
permissions: {
|
||||
view: true,
|
||||
edit: true,
|
||||
delete: false,
|
||||
download: true,
|
||||
share: true
|
||||
},
|
||||
authentication: {
|
||||
ssoEnabled: false,
|
||||
mfaRequired: false,
|
||||
ldapIntegration: false,
|
||||
sessionTimeout: 8
|
||||
}
|
||||
},
|
||||
|
||||
// Document & Folder Settings
|
||||
documents: {
|
||||
folderHierarchy: {
|
||||
maxDepth: 5,
|
||||
defaultStructure: ['Department', 'Project', 'Category', 'Year'],
|
||||
folderTemplates: ['Standard', 'Project-based', 'Department-based']
|
||||
},
|
||||
namingConventions: {
|
||||
autoGenerate: true,
|
||||
mandatoryFields: ['title', 'department', 'date'],
|
||||
pattern: '{department}_{title}_{date}'
|
||||
},
|
||||
retention: {
|
||||
enabled: true,
|
||||
defaultDays: 2555, // 7 years
|
||||
archiveBeforeDelete: true
|
||||
},
|
||||
versionControl: {
|
||||
enabled: true,
|
||||
maxVersions: 10,
|
||||
autoVersioning: true
|
||||
}
|
||||
},
|
||||
|
||||
// Metadata & Tagging
|
||||
metadata: {
|
||||
customFields: [
|
||||
{ name: 'Department', type: 'dropdown', required: true },
|
||||
{ name: 'Priority', type: 'select', required: false },
|
||||
{ name: 'Project Code', type: 'text', required: true },
|
||||
{ name: 'Review Date', type: 'date', required: false }
|
||||
],
|
||||
tagging: {
|
||||
predefinedTags: ['urgent', 'confidential', 'public', 'draft', 'final'],
|
||||
userGeneratedTags: true,
|
||||
tagSuggestions: true
|
||||
},
|
||||
classification: {
|
||||
autoClassification: true,
|
||||
rules: ['confidential-keywords', 'department-based', 'file-type']
|
||||
}
|
||||
},
|
||||
|
||||
// Workflow & Automation
|
||||
workflow: {
|
||||
approvalFlows: {
|
||||
enabled: true,
|
||||
defaultFlow: 'department-head-approval',
|
||||
customFlows: ['legal-review', 'finance-approval', 'director-sign-off']
|
||||
},
|
||||
notifications: {
|
||||
emailNotifications: true,
|
||||
inAppNotifications: true,
|
||||
uploadAlerts: true,
|
||||
deadlineReminders: true
|
||||
},
|
||||
automation: {
|
||||
triggers: ['document-uploaded', 'approval-completed', 'deadline-reached'],
|
||||
actions: ['move-to-folder', 'send-notification', 'create-task']
|
||||
}
|
||||
},
|
||||
|
||||
// Upload & Storage Settings
|
||||
upload: {
|
||||
fileTypes: {
|
||||
allowed: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'png'],
|
||||
blocked: ['exe', 'bat', 'cmd']
|
||||
},
|
||||
fileSizeLimit: 100, // MB
|
||||
quotas: {
|
||||
perUser: 5000, // MB
|
||||
perGroup: 50000, // MB
|
||||
perProject: 100000 // MB
|
||||
},
|
||||
storage: {
|
||||
type: 'local', // local, s3, azure, google
|
||||
path: '/var/uploads/edms',
|
||||
backupEnabled: true,
|
||||
compressionEnabled: false
|
||||
}
|
||||
},
|
||||
|
||||
// System Settings
|
||||
system: {
|
||||
timezone: 'Asia/Kuala_Lumpur',
|
||||
backupSchedule: 'daily',
|
||||
logLevel: 'info',
|
||||
maintenanceMode: false,
|
||||
autoUpdates: false,
|
||||
systemMonitoring: true,
|
||||
performanceMetrics: true
|
||||
}
|
||||
}));
|
||||
|
||||
// Loading state
|
||||
const loading = useState('dmsSettingsLoading', () => false);
|
||||
const saving = useState('dmsSettingsSaving', () => false);
|
||||
|
||||
// Load DMS settings from API
|
||||
const loadDmsSettings = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await $fetch("/api/dms/settings", {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
dmsSettings.value = { ...dmsSettings.value, ...response.data };
|
||||
console.log('[useDmsSettings] Settings loaded successfully:', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading DMS settings:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Update DMS settings
|
||||
const updateDmsSettings = async (newSettings) => {
|
||||
console.log("[useDmsSettings] updateDmsSettings called with:", JSON.parse(JSON.stringify(newSettings)));
|
||||
saving.value = true;
|
||||
try {
|
||||
const response = await $fetch("/api/dms/settings", {
|
||||
method: "POST",
|
||||
body: newSettings,
|
||||
});
|
||||
console.log("[useDmsSettings] API response received:", JSON.parse(JSON.stringify(response)));
|
||||
|
||||
if (response && response.statusCode === 200) {
|
||||
// Reload settings after successful update
|
||||
await loadDmsSettings();
|
||||
console.log("[useDmsSettings] Returning success from updateDmsSettings.");
|
||||
return { success: true, data: response.data };
|
||||
}
|
||||
|
||||
let errorMessage = "Update operation failed: No data returned from server.";
|
||||
if (response && typeof response === 'object' && response !== null && 'message' in response) {
|
||||
errorMessage = response.message;
|
||||
}
|
||||
|
||||
console.log("[useDmsSettings] Returning failure from updateDmsSettings:", errorMessage);
|
||||
return { success: false, error: { message: errorMessage, details: response } };
|
||||
} catch (error) {
|
||||
console.error("[useDmsSettings] Error in updateDmsSettings catch block:", error);
|
||||
let detailedMessage = "An unexpected error occurred during update.";
|
||||
if (error.data && error.data.message) {
|
||||
detailedMessage = error.data.message;
|
||||
} else if (error.message) {
|
||||
detailedMessage = error.message;
|
||||
}
|
||||
console.log("[useDmsSettings] Returning failure (catch block) from updateDmsSettings:", detailedMessage);
|
||||
return { success: false, error: { message: detailedMessage, details: error } };
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Reset settings to defaults
|
||||
const resetToDefaults = async () => {
|
||||
const defaultSettings = {
|
||||
access: {
|
||||
userRoles: ['Admin', 'Editor', 'Viewer', 'Uploader'],
|
||||
rbacEnabled: true,
|
||||
userGroups: ['HR Department', 'Finance', 'IT', 'Legal'],
|
||||
permissions: {
|
||||
view: true,
|
||||
edit: true,
|
||||
delete: false,
|
||||
download: true,
|
||||
share: true
|
||||
},
|
||||
authentication: {
|
||||
ssoEnabled: false,
|
||||
mfaRequired: false,
|
||||
ldapIntegration: false,
|
||||
sessionTimeout: 8
|
||||
}
|
||||
},
|
||||
documents: {
|
||||
folderHierarchy: {
|
||||
maxDepth: 5,
|
||||
defaultStructure: ['Department', 'Project', 'Category', 'Year'],
|
||||
folderTemplates: ['Standard', 'Project-based', 'Department-based']
|
||||
},
|
||||
namingConventions: {
|
||||
autoGenerate: true,
|
||||
mandatoryFields: ['title', 'department', 'date'],
|
||||
pattern: '{department}_{title}_{date}'
|
||||
},
|
||||
retention: {
|
||||
enabled: true,
|
||||
defaultDays: 2555,
|
||||
archiveBeforeDelete: true
|
||||
},
|
||||
versionControl: {
|
||||
enabled: true,
|
||||
maxVersions: 10,
|
||||
autoVersioning: true
|
||||
}
|
||||
},
|
||||
metadata: {
|
||||
customFields: [
|
||||
{ name: 'Department', type: 'dropdown', required: true },
|
||||
{ name: 'Priority', type: 'select', required: false },
|
||||
{ name: 'Project Code', type: 'text', required: true },
|
||||
{ name: 'Review Date', type: 'date', required: false }
|
||||
],
|
||||
tagging: {
|
||||
predefinedTags: ['urgent', 'confidential', 'public', 'draft', 'final'],
|
||||
userGeneratedTags: true,
|
||||
tagSuggestions: true
|
||||
},
|
||||
classification: {
|
||||
autoClassification: true,
|
||||
rules: ['confidential-keywords', 'department-based', 'file-type']
|
||||
}
|
||||
},
|
||||
workflow: {
|
||||
approvalFlows: {
|
||||
enabled: true,
|
||||
defaultFlow: 'department-head-approval',
|
||||
customFlows: ['legal-review', 'finance-approval', 'director-sign-off']
|
||||
},
|
||||
notifications: {
|
||||
emailNotifications: true,
|
||||
inAppNotifications: true,
|
||||
uploadAlerts: true,
|
||||
deadlineReminders: true
|
||||
},
|
||||
automation: {
|
||||
triggers: ['document-uploaded', 'approval-completed', 'deadline-reached'],
|
||||
actions: ['move-to-folder', 'send-notification', 'create-task']
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
fileTypes: {
|
||||
allowed: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'png'],
|
||||
blocked: ['exe', 'bat', 'cmd']
|
||||
},
|
||||
fileSizeLimit: 100,
|
||||
quotas: {
|
||||
perUser: 5000,
|
||||
perGroup: 50000,
|
||||
perProject: 100000
|
||||
},
|
||||
storage: {
|
||||
type: 'local',
|
||||
path: '/var/uploads/edms',
|
||||
backupEnabled: true,
|
||||
compressionEnabled: false
|
||||
}
|
||||
},
|
||||
system: {
|
||||
timezone: 'Asia/Kuala_Lumpur',
|
||||
backupSchedule: 'daily',
|
||||
logLevel: 'info',
|
||||
maintenanceMode: false,
|
||||
autoUpdates: false,
|
||||
systemMonitoring: true,
|
||||
performanceMetrics: true
|
||||
}
|
||||
};
|
||||
|
||||
return await updateDmsSettings(defaultSettings);
|
||||
};
|
||||
|
||||
// Export settings to JSON
|
||||
const exportSettings = () => {
|
||||
const dataStr = JSON.stringify(dmsSettings.value, null, 2);
|
||||
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
||||
|
||||
const exportFileDefaultName = 'dms-settings.json';
|
||||
|
||||
const linkElement = document.createElement('a');
|
||||
linkElement.setAttribute('href', dataUri);
|
||||
linkElement.setAttribute('download', exportFileDefaultName);
|
||||
linkElement.click();
|
||||
};
|
||||
|
||||
// Import settings from JSON
|
||||
const importSettings = (jsonData) => {
|
||||
try {
|
||||
const importedSettings = JSON.parse(jsonData);
|
||||
dmsSettings.value = { ...dmsSettings.value, ...importedSettings };
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error importing settings:', error);
|
||||
return { success: false, error: 'Invalid JSON format' };
|
||||
}
|
||||
};
|
||||
|
||||
// Get setting by category and key
|
||||
const getSetting = (category, key) => {
|
||||
if (dmsSettings.value[category]) {
|
||||
return dmsSettings.value[category][key];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Update specific setting
|
||||
const updateSetting = async (category, key, value) => {
|
||||
if (dmsSettings.value[category]) {
|
||||
dmsSettings.value[category][key] = value;
|
||||
// Save to backend
|
||||
return await updateDmsSettings(dmsSettings.value);
|
||||
}
|
||||
return { success: false, error: 'Category not found' };
|
||||
};
|
||||
|
||||
return {
|
||||
dmsSettings: readonly(dmsSettings),
|
||||
loading: readonly(loading),
|
||||
saving: readonly(saving),
|
||||
loadDmsSettings,
|
||||
updateDmsSettings,
|
||||
resetToDefaults,
|
||||
exportSettings,
|
||||
importSettings,
|
||||
getSetting,
|
||||
updateSetting
|
||||
};
|
||||
};
|
1019
docs/DMS_SETTINGS.md
Normal file
1019
docs/DMS_SETTINGS.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,161 +1,656 @@
|
||||
# Site Settings Feature
|
||||
# Site Settings System - Comprehensive Guide
|
||||
|
||||
## Overview
|
||||
The Site Settings feature allows administrators to customize the appearance and branding of the application through a user-friendly interface. All settings are globally applied across the entire application including SEO, meta tags, and visual elements.
|
||||
The Site Settings system provides administrators with comprehensive control over the visual appearance, branding, and global configuration of the Electronic Document Management System (EDMS). This system allows complete customization of the application's look and feel, SEO settings, and theme management through an intuitive web interface.
|
||||
|
||||
## Features
|
||||
## Table of Contents
|
||||
1. [System Integration](#system-integration)
|
||||
2. [Features Overview](#features-overview)
|
||||
3. [Accessing Site Settings](#accessing-site-settings)
|
||||
4. [Configuration Categories](#configuration-categories)
|
||||
5. [Basic Information Settings](#basic-information-settings)
|
||||
6. [Branding & Visual Identity](#branding--visual-identity)
|
||||
7. [SEO & Meta Tag Management](#seo--meta-tag-management)
|
||||
8. [Theme System Integration](#theme-system-integration)
|
||||
9. [Advanced Customization](#advanced-customization)
|
||||
10. [API Integration](#api-integration)
|
||||
11. [Technical Implementation](#technical-implementation)
|
||||
12. [Best Practices](#best-practices)
|
||||
13. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## System Integration
|
||||
|
||||
### EDMS Ecosystem
|
||||
The Site Settings system is fully integrated with the EDMS ecosystem and works alongside:
|
||||
- **DMS Settings**: Document management configuration (separate system)
|
||||
- **User Management**: Role-based access control for settings access
|
||||
- **Theme System**: Dynamic theme switching and custom theme support
|
||||
- **Component Library**: Rs component system integration
|
||||
- **Global State**: Pinia store management for reactive updates
|
||||
|
||||
### Global Impact
|
||||
Site settings affect the entire application including:
|
||||
- **Header and Navigation**: Site logo, name, and branding elements
|
||||
- **Loading Screens**: Custom loading logos and branding
|
||||
- **Document Viewer**: Consistent branding across all interfaces
|
||||
- **Authentication Pages**: Login page branding and styling
|
||||
- **SEO Elements**: Meta tags, Open Graph, and social media integration
|
||||
- **Theme Application**: Global color schemes and visual styling
|
||||
|
||||
## Features Overview
|
||||
|
||||
### Core Capabilities
|
||||
- **Complete Branding Control**: Logos, site name, and visual identity
|
||||
- **Theme Management**: Built-in themes plus custom theme support
|
||||
- **SEO Optimization**: Comprehensive meta tag and social media optimization
|
||||
- **Custom Styling**: CSS injection and custom theme file support
|
||||
- **Real-time Preview**: Live preview of changes before applying
|
||||
- **Global Integration**: Settings applied across entire application
|
||||
- **Backup & Restore**: Export/import configuration for backup and migration
|
||||
|
||||
### Administrative Features
|
||||
- **Role-Based Access**: Only administrators can modify site settings
|
||||
- **Validation System**: Comprehensive validation for all settings
|
||||
- **File Upload Management**: Secure file upload for logos and assets
|
||||
- **Version Control**: Track changes and maintain configuration history
|
||||
- **Multi-Environment Support**: Different configurations for different environments
|
||||
|
||||
## Accessing Site Settings
|
||||
|
||||
### Prerequisites
|
||||
- **Administrative Privileges**: User must have admin role
|
||||
- **Modern Browser**: Chrome 70+, Firefox 65+, Safari 12+, Edge 79+
|
||||
- **JavaScript Enabled**: Required for interactive functionality
|
||||
- **Network Access**: Stable connection for file uploads and saves
|
||||
|
||||
### Navigation Path
|
||||
1. **Main Menu**: Navigate to **Pentadbiran** (Administration)
|
||||
2. **Configuration Section**: Select **Konfigurasi** (Configuration)
|
||||
3. **Site Settings**: Choose **Site Settings** from the submenu
|
||||
4. **Alternative Access**: Direct URL navigation to `/admin/site-settings`
|
||||
|
||||
### Interface Layout
|
||||
- **Tabbed Interface**: Basic Info, Branding, SEO, Advanced settings
|
||||
- **Live Preview Panel**: Real-time preview of changes
|
||||
- **Action Buttons**: Save, Reset, Export, Import functionality
|
||||
- **Validation Feedback**: Immediate feedback on invalid settings
|
||||
- **Loading States**: Visual feedback during save operations
|
||||
|
||||
## Configuration Categories
|
||||
|
||||
### 1. Basic Information
|
||||
- **Site Name**: Customize the application name displayed globally in:
|
||||
- Header and sidebar
|
||||
- Browser title and meta tags
|
||||
- SEO and Open Graph tags
|
||||
- Loading screen
|
||||
- All pages and components
|
||||
- **Site Description**: Set a description used for:
|
||||
- SEO meta descriptions
|
||||
- Open Graph descriptions
|
||||
- Twitter Card descriptions
|
||||
- **Theme Selection**: Choose from available themes:
|
||||
- Standard themes (from themeList.js)
|
||||
- Accessibility themes (from themeList2.js)
|
||||
- Custom themes added to theme.css
|
||||
- **Site Name**: Application name displayed globally
|
||||
- **Site Description**: Meta description for SEO and social sharing
|
||||
- **Font Configuration**: Typography settings and font selection
|
||||
- **Display Options**: Show/hide site name in header and other locations
|
||||
|
||||
### 2. Branding
|
||||
- **Site Logo**: Upload a custom logo displayed in:
|
||||
- Header (horizontal layout)
|
||||
- Sidebar (vertical layout)
|
||||
- Loading screen
|
||||
- Login page
|
||||
- Any component using site settings
|
||||
- **Favicon**: Upload a custom favicon displayed in:
|
||||
- Browser tabs
|
||||
- Bookmarks
|
||||
- Mobile home screen icons
|
||||
- **Site Logo**: Main application logo for headers and navigation
|
||||
- **Loading Logo**: Branded loading screen logo
|
||||
- **Favicon**: Browser tab icon and bookmark icon
|
||||
- **Login Logo**: Dedicated branding for authentication pages
|
||||
|
||||
### 3. Advanced Settings
|
||||
- **Custom CSS**: Add custom CSS injected into document head
|
||||
- **Custom Theme File**: Upload CSS files saved to `/assets/style/css/`
|
||||
- **Add Custom Theme to theme.css**: Directly add themes to the main theme.css file
|
||||
### 3. SEO & Social Media
|
||||
- **Meta Tags**: Title, description, keywords, author information
|
||||
- **Open Graph**: Facebook and social media sharing optimization
|
||||
- **Twitter Cards**: Twitter-specific meta tag configuration
|
||||
- **Analytics Integration**: Google Analytics, Tag Manager, Facebook Pixel
|
||||
|
||||
## How to Access
|
||||
### 4. Advanced Settings
|
||||
- **Custom CSS**: Global CSS injection for advanced styling
|
||||
- **Custom Themes**: Upload and manage custom theme files
|
||||
- **Theme Editor**: Direct editing of theme.css file
|
||||
- **Developer Tools**: Advanced configuration options
|
||||
|
||||
1. Navigate to **Pentadbiran** → **Konfigurasi** → **Site Settings**
|
||||
2. Use the tabbed interface:
|
||||
- **Basic Info**: Site name, description, and theme selection
|
||||
- **Branding**: Logo and favicon uploads
|
||||
- **Advanced**: Custom CSS and theme management
|
||||
3. Use the **Live Preview** panel to see changes in real-time
|
||||
4. Click **Save Changes** to apply your settings
|
||||
## Basic Information Settings
|
||||
|
||||
## Technical Implementation
|
||||
### Site Name Configuration
|
||||
- **Purpose**: Define the application name displayed throughout the system
|
||||
- **Default Value**: "corradAF" (customizable)
|
||||
- **Global Impact**: Updates header, page titles, meta tags, loading screens
|
||||
- **Font Size Control**: Adjustable font size (12-36px range)
|
||||
- **Character Limit**: Recommended maximum of 50 characters for optimal display
|
||||
|
||||
### Database Schema
|
||||
The settings are stored in the `site_settings` table with the following fields:
|
||||
- `siteName`, `siteDescription`
|
||||
- `siteLogo`, `siteFavicon`
|
||||
- `selectedTheme` - Selected theme name
|
||||
- `customCSS`, `customThemeFile`
|
||||
- Legacy fields maintained for backward compatibility
|
||||
**Display Locations**:
|
||||
- Header navigation (primary logo area)
|
||||
- Browser page titles (prefixed to page names)
|
||||
- Loading screen branding
|
||||
- SEO meta tags and social sharing
|
||||
- Authentication page headers
|
||||
|
||||
### API Endpoints
|
||||
- `GET /api/devtool/config/site-settings` - Retrieve current settings
|
||||
- `POST /api/devtool/config/site-settings` - Update settings
|
||||
- `POST /api/devtool/config/upload-file` - Upload files (logos, themes)
|
||||
- `POST /api/devtool/config/add-custom-theme` - Add custom theme to theme.css
|
||||
### Site Description
|
||||
- **Purpose**: Provide descriptive text for SEO and social media sharing
|
||||
- **SEO Impact**: Used as meta description tag for search engines
|
||||
- **Social Sharing**: Appears in social media link previews
|
||||
- **Character Limit**: Recommended 150-160 characters for optimal SEO
|
||||
- **Multi-language Support**: Single description applies globally
|
||||
|
||||
### File Upload Locations
|
||||
- **Logo and Favicon files**: Saved to `public/uploads/site-settings/`
|
||||
- **Theme CSS files**: Saved to `assets/style/css/` directory
|
||||
- **Custom themes**: Added directly to `assets/style/css/base/theme.css`
|
||||
### Typography Configuration
|
||||
- **Font Selection**: Choose from available system fonts
|
||||
- **Font Source**: Local fonts vs. web fonts (Google Fonts integration)
|
||||
- **Font Size Controls**: Configurable sizes for different elements
|
||||
- **Global Application**: Typography changes apply system-wide
|
||||
|
||||
### Composable
|
||||
The `useSiteSettings()` composable provides:
|
||||
- `siteSettings` - Reactive settings object
|
||||
- `loadSiteSettings()` - Load settings from API
|
||||
- `updateSiteSettings()` - Update settings
|
||||
- `setTheme()` - Set theme using existing theme system
|
||||
- `getCurrentTheme()` - Get current theme
|
||||
- `applyThemeSettings()` - Apply theme changes to DOM
|
||||
- `updateGlobalMeta()` - Update global meta tags and SEO
|
||||
- `addCustomThemeToFile()` - Add custom theme to theme.css
|
||||
## Branding & Visual Identity
|
||||
|
||||
### Global Integration
|
||||
The site settings are globally integrated across:
|
||||
### Logo Management System
|
||||
|
||||
#### Header Component
|
||||
- Uses site settings for logo and name display
|
||||
- Theme selection dropdown uses same system as site settings
|
||||
- Synced with site settings theme selection
|
||||
#### Site Logo (Primary)
|
||||
- **Usage**: Main application logo in header and navigation
|
||||
- **Recommended Dimensions**: 200x60px (maximum)
|
||||
- **Supported Formats**: PNG, JPG, SVG (vector preferred)
|
||||
- **File Size Limit**: 5MB maximum
|
||||
- **Responsive Behavior**: Automatic scaling for different screen sizes
|
||||
- **Fallback**: System default logo if custom logo fails to load
|
||||
|
||||
#### Loading Component
|
||||
- Uses site logo if available, fallback to default
|
||||
- Displays site name in loading screen
|
||||
#### Loading Logo
|
||||
- **Usage**: Branded loading screens and splash pages
|
||||
- **Recommended Dimensions**: 100x100px (square preferred)
|
||||
- **Animation Support**: Static images only (CSS animations can be added)
|
||||
- **Multiple Screens**: Applied to all loading states throughout application
|
||||
- **Brand Consistency**: Should complement primary site logo
|
||||
|
||||
#### App.vue
|
||||
- Global meta tags updated from site settings
|
||||
- Title, description, and favicon managed globally
|
||||
- Theme initialization from site settings
|
||||
#### Favicon Configuration
|
||||
- **Usage**: Browser tab icons, bookmarks, mobile home screen icons
|
||||
- **Required Format**: ICO format preferred (PNG acceptable)
|
||||
- **Dimensions**: 16x16, 32x32, 48x48px (multi-size ICO recommended)
|
||||
- **Mobile Icons**: Apple touch icons and Android home screen support
|
||||
- **Browser Compatibility**: Cross-browser favicon support
|
||||
|
||||
#### SEO and Meta Tags
|
||||
- Document title updated globally
|
||||
- Meta descriptions for SEO
|
||||
- Open Graph tags for social sharing
|
||||
- Twitter Card tags
|
||||
- Favicon and apple-touch-icon
|
||||
#### Login Page Logo
|
||||
- **Usage**: Dedicated branding for authentication and login pages
|
||||
- **Design Consideration**: Can be different from main site logo for branding flexibility
|
||||
- **Recommended Dimensions**: 250x80px maximum
|
||||
- **Security Context**: Appears on security-sensitive pages
|
||||
- **Brand Trust**: Important for user trust and brand recognition
|
||||
|
||||
### Theme System Integration
|
||||
- Integrates with existing theme system (themeList.js, themeList2.js)
|
||||
- Theme selection in header dropdown synced with site settings
|
||||
- Custom themes can be added directly to theme.css
|
||||
- Backward compatibility with existing theme structure
|
||||
### File Upload System
|
||||
- **Secure Upload**: File validation and virus scanning
|
||||
- **Storage Location**: `public/uploads/site-settings/` directory
|
||||
- **Backup Integration**: Uploaded files included in system backups
|
||||
- **Version Control**: Maintain previous versions of uploaded assets
|
||||
- **CDN Support**: Compatible with content delivery networks
|
||||
|
||||
### Custom Theme Structure
|
||||
Custom themes added to theme.css should follow this structure:
|
||||
## SEO & Meta Tag Management
|
||||
|
||||
### Search Engine Optimization
|
||||
|
||||
#### Basic SEO Settings
|
||||
- **SEO Title**: Custom title for search engine results
|
||||
- **Meta Description**: Description displayed in search results
|
||||
- **Meta Keywords**: Keyword tags (legacy but still supported)
|
||||
- **Meta Author**: Content author information
|
||||
- **Canonical URL**: Preferred URL for content indexing
|
||||
|
||||
#### Advanced SEO Configuration
|
||||
- **Robots Meta Tag**: Control search engine crawling behavior
|
||||
- Default: "index, follow" (allow indexing and link following)
|
||||
- Options: noindex, nofollow, noarchive, nosnippet
|
||||
- **Structured Data**: Schema.org markup for rich snippets
|
||||
- **XML Sitemap**: Automatic sitemap generation and submission
|
||||
- **Page Speed Optimization**: Settings that impact page load times
|
||||
|
||||
### Social Media Integration
|
||||
|
||||
#### Open Graph (Facebook)
|
||||
- **og:title**: Social media sharing title
|
||||
- **og:description**: Social media sharing description
|
||||
- **og:image**: Image displayed in social media previews
|
||||
- **og:type**: Content type (website, article, etc.)
|
||||
- **og:url**: Canonical URL for social sharing
|
||||
|
||||
#### Twitter Cards
|
||||
- **twitter:card**: Card type (summary, summary_large_image, app, player)
|
||||
- **twitter:title**: Twitter-specific sharing title
|
||||
- **twitter:description**: Twitter-specific sharing description
|
||||
- **twitter:image**: Twitter preview image
|
||||
- **twitter:creator**: Twitter handle of content creator
|
||||
|
||||
### Analytics & Tracking
|
||||
|
||||
#### Google Analytics Integration
|
||||
- **Tracking ID**: Google Analytics measurement ID (GA4 format)
|
||||
- **Enhanced Ecommerce**: Advanced tracking for document interactions
|
||||
- **Custom Events**: Track document downloads, views, searches
|
||||
- **Privacy Compliance**: GDPR and privacy regulation compliance
|
||||
|
||||
#### Google Tag Manager
|
||||
- **Container ID**: GTM container identifier
|
||||
- **Custom Variables**: Document metadata as GTM variables
|
||||
- **Event Tracking**: Advanced event tracking through GTM
|
||||
- **Third-party Integration**: Easy integration with other analytics tools
|
||||
|
||||
#### Facebook Pixel
|
||||
- **Pixel ID**: Facebook advertising pixel identifier
|
||||
- **Conversion Tracking**: Track document interactions as conversions
|
||||
- **Custom Audiences**: Build audiences based on document engagement
|
||||
- **Privacy Controls**: Respect user privacy preferences
|
||||
|
||||
## Theme System Integration
|
||||
|
||||
### Built-in Theme Support
|
||||
|
||||
#### Standard Themes
|
||||
The system includes several pre-built themes from `themeList.js`:
|
||||
- **biasa**: Default neutral theme with balanced colors
|
||||
- **terang**: Light theme with bright, clean aesthetics
|
||||
- **gelap**: Dark theme for reduced eye strain
|
||||
- **biru**: Blue-dominant professional theme
|
||||
- **hijau**: Green nature-inspired theme
|
||||
|
||||
#### Accessibility Themes
|
||||
Special themes from `themeList2.js` for enhanced accessibility:
|
||||
- **high-contrast**: High contrast for visual impairments
|
||||
- **large-text**: Increased font sizes for readability
|
||||
- **color-blind**: Color-blind friendly color palettes
|
||||
- **low-vision**: Optimized for users with low vision
|
||||
|
||||
### Custom Theme Development
|
||||
|
||||
#### Theme Structure
|
||||
Custom themes follow CSS custom property structure:
|
||||
```css
|
||||
html[data-theme="your-theme-name"] {
|
||||
html[data-theme="custom-theme-name"] {
|
||||
--color-primary: 255, 0, 0;
|
||||
--color-secondary: 0, 255, 0;
|
||||
--color-success: 0, 255, 0;
|
||||
--color-info: 0, 0, 255;
|
||||
--color-warning: 255, 255, 0;
|
||||
--color-danger: 255, 0, 0;
|
||||
/* Add your theme variables here */
|
||||
--color-light: 248, 249, 250;
|
||||
--color-dark: 33, 37, 41;
|
||||
/* Additional custom properties */
|
||||
}
|
||||
```
|
||||
|
||||
## Default Values
|
||||
If no settings are configured, the system uses these defaults:
|
||||
- Site Name: "corradAF"
|
||||
- Site Description: "corradAF Base Project"
|
||||
- Selected Theme: "biasa"
|
||||
- Logo: Default corradAF logo
|
||||
- Favicon: Default favicon
|
||||
#### Color Variable System
|
||||
- **RGB Values**: Colors defined as RGB triplets for alpha transparency support
|
||||
- **Semantic Naming**: Colors named by purpose rather than appearance
|
||||
- **Component Integration**: Variables used throughout Rs component system
|
||||
- **Dark Mode Support**: Automatic dark mode variants
|
||||
|
||||
## Migration Notes
|
||||
- Legacy color fields (primaryColor, secondaryColor, etc.) are maintained for backward compatibility
|
||||
- `themeMode` field is mapped to `selectedTheme` for compatibility
|
||||
- Existing installations will automatically use default values
|
||||
- Theme selection integrates with existing theme dropdown in header
|
||||
#### Theme File Management
|
||||
- **Upload Location**: Custom themes saved to `assets/style/css/`
|
||||
- **Integration Method**: Direct injection into `theme.css` file
|
||||
- **Validation**: CSS syntax validation before integration
|
||||
- **Backup**: Automatic backup before theme modifications
|
||||
|
||||
## Notes
|
||||
- Changes are applied immediately in the preview
|
||||
- Theme changes affect the entire application
|
||||
- Custom CSS is injected into the document head
|
||||
- Theme files are saved to `/assets/style/css/` for proper integration
|
||||
- File uploads are validated for type and size
|
||||
- Settings persist across browser sessions
|
||||
- Site name and description updates are reflected globally and immediately
|
||||
- All meta tags and SEO elements are automatically updated
|
||||
- Logo changes are reflected in all components that use site settings
|
||||
### Theme Switching Mechanism
|
||||
- **Real-time Application**: Themes applied immediately without page refresh
|
||||
- **Header Integration**: Theme selector synchronized with site settings
|
||||
- **User Preferences**: Individual user theme preferences (if enabled)
|
||||
- **Default Theme**: System-wide default theme setting
|
||||
|
||||
### Important Notes
|
||||
- Changes are applied immediately in the preview
|
||||
- Theme changes affect the entire application
|
||||
- Custom CSS is injected into the document head
|
||||
- Theme files are saved to `/assets/style/css/` for proper integration
|
||||
- File uploads are validated for type and size
|
||||
- Settings persist across browser sessions
|
||||
- Site name and description updates are reflected globally and immediately
|
||||
- All meta tags and SEO elements are automatically updated
|
||||
- Logo changes are reflected in all components that use site settings
|
||||
## Advanced Customization
|
||||
|
||||
### Custom CSS Injection
|
||||
|
||||
#### Global CSS Override
|
||||
- **Injection Point**: CSS inserted into document `<head>` element
|
||||
- **Priority**: Custom CSS has high specificity to override defaults
|
||||
- **Validation**: Basic CSS syntax validation
|
||||
- **Performance**: Minification and optimization of injected CSS
|
||||
|
||||
#### Best Practices for Custom CSS
|
||||
- **Specificity**: Use appropriate CSS specificity for overrides
|
||||
- **Performance**: Minimize custom CSS for better performance
|
||||
- **Maintenance**: Document custom CSS changes for future reference
|
||||
- **Testing**: Test across different browsers and devices
|
||||
|
||||
#### CSS Framework Integration
|
||||
- **TailwindCSS Compatibility**: Custom CSS works alongside TailwindCSS
|
||||
- **Component System**: Respect Rs component styling patterns
|
||||
- **Responsive Design**: Ensure custom CSS is responsive
|
||||
- **Dark Mode**: Consider dark mode implications
|
||||
|
||||
### Custom Theme File Upload
|
||||
|
||||
#### File Requirements
|
||||
- **Format**: Standard CSS files (.css extension)
|
||||
- **Size Limit**: 1MB maximum file size
|
||||
- **Encoding**: UTF-8 encoding required
|
||||
- **Syntax**: Valid CSS syntax required
|
||||
|
||||
#### Integration Process
|
||||
1. **File Upload**: Secure file upload to server
|
||||
2. **Validation**: CSS syntax and security validation
|
||||
3. **Integration**: Append to main theme.css file
|
||||
4. **Backup**: Create backup of previous theme.css
|
||||
5. **Application**: Theme immediately available for selection
|
||||
|
||||
#### Security Considerations
|
||||
- **Content Filtering**: Remove potentially dangerous CSS
|
||||
- **Path Restrictions**: Prevent access to restricted file paths
|
||||
- **Size Limits**: Prevent resource exhaustion attacks
|
||||
- **Validation**: Comprehensive CSS validation
|
||||
|
||||
## API Integration
|
||||
|
||||
### Site Settings API Endpoints
|
||||
|
||||
#### GET `/api/devtool/config/site-settings`
|
||||
**Purpose**: Retrieve current site settings configuration
|
||||
|
||||
**Response Format**:
|
||||
```json
|
||||
{
|
||||
"statusCode": 200,
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"siteName": "Custom EDMS",
|
||||
"siteDescription": "Enterprise Document Management System",
|
||||
"siteLogo": "/uploads/site-settings/logo.png",
|
||||
"siteFavicon": "/uploads/site-settings/favicon.ico",
|
||||
"selectedTheme": "professional-blue",
|
||||
"customCSS": ".custom-header { background: #blue; }",
|
||||
"seoTitle": "EDMS - Document Management",
|
||||
"seoDescription": "Comprehensive document management solution",
|
||||
"seoKeywords": "document, management, enterprise",
|
||||
"seoGoogleAnalytics": "GA-XXXXXXXXX-X"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### POST `/api/devtool/config/site-settings`
|
||||
**Purpose**: Update site settings configuration
|
||||
|
||||
**Request Format**:
|
||||
```json
|
||||
{
|
||||
"siteName": "Updated EDMS Name",
|
||||
"siteDescription": "Updated description",
|
||||
"selectedTheme": "dark-professional",
|
||||
"customCSS": ".updated-styles { color: red; }",
|
||||
"seoTitle": "Updated SEO Title"
|
||||
}
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Partial updates supported (only changed fields required)
|
||||
- Automatic validation of all input data
|
||||
- File path validation for security
|
||||
- Database persistence with timestamps
|
||||
|
||||
#### POST `/api/devtool/config/upload-file`
|
||||
**Purpose**: Secure file upload for logos and assets
|
||||
|
||||
**Request Format**: Multipart form data with file and metadata
|
||||
|
||||
**Response Format**:
|
||||
```json
|
||||
{
|
||||
"statusCode": 200,
|
||||
"message": "File uploaded successfully",
|
||||
"data": {
|
||||
"filename": "uploaded-logo.png",
|
||||
"path": "/uploads/site-settings/logo.png",
|
||||
"size": 15420,
|
||||
"type": "image/png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Security Features**:
|
||||
- File type validation (images only for logos)
|
||||
- File size limits enforcement
|
||||
- Virus scanning integration
|
||||
- Secure file storage with proper permissions
|
||||
|
||||
#### POST `/api/devtool/config/add-custom-theme`
|
||||
**Purpose**: Add custom theme directly to theme.css file
|
||||
|
||||
**Request Format**:
|
||||
```json
|
||||
{
|
||||
"themeName": "custom-corporate",
|
||||
"themeCSS": "html[data-theme=\"custom-corporate\"] { --color-primary: 0, 100, 200; }"
|
||||
}
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- CSS validation before integration
|
||||
- Automatic backup of existing theme.css
|
||||
- Immediate availability after integration
|
||||
- Rollback capability if integration fails
|
||||
|
||||
### Composable Integration
|
||||
|
||||
#### useSiteSettings() Composable
|
||||
The `useSiteSettings()` composable provides reactive state management:
|
||||
|
||||
```javascript
|
||||
const {
|
||||
siteSettings, // Reactive settings object
|
||||
loading, // Loading state
|
||||
loadSiteSettings, // Load settings from API
|
||||
updateSiteSettings, // Update settings
|
||||
setTheme, // Apply theme changes
|
||||
getCurrentTheme, // Get current theme
|
||||
applyThemeSettings, // Apply theme to DOM
|
||||
updateGlobalMeta, // Update meta tags
|
||||
addCustomThemeToFile // Add custom theme
|
||||
} = useSiteSettings();
|
||||
```
|
||||
|
||||
#### Real-time Updates
|
||||
- **Reactive State**: Changes immediately reflected in UI
|
||||
- **Global Sync**: Updates synchronized across all components
|
||||
- **Meta Tag Updates**: SEO meta tags updated dynamically
|
||||
- **Theme Application**: Theme changes applied without page refresh
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Database Schema
|
||||
Settings stored in `site_settings` table with comprehensive field structure:
|
||||
|
||||
```sql
|
||||
CREATE TABLE site_settings (
|
||||
settingID INT PRIMARY KEY AUTO_INCREMENT,
|
||||
siteName VARCHAR(255) DEFAULT 'corradAF',
|
||||
siteNameFontSize INT DEFAULT 18,
|
||||
siteDescription TEXT,
|
||||
siteLogo VARCHAR(500),
|
||||
siteLoadingLogo VARCHAR(500),
|
||||
siteFavicon VARCHAR(500),
|
||||
siteLoginLogo VARCHAR(500),
|
||||
showSiteNameInHeader BOOLEAN DEFAULT TRUE,
|
||||
customCSS LONGTEXT,
|
||||
themeMode VARCHAR(100) DEFAULT 'biasa',
|
||||
customThemeFile VARCHAR(500),
|
||||
currentFont VARCHAR(100),
|
||||
fontSource VARCHAR(100),
|
||||
seoTitle VARCHAR(255),
|
||||
seoDescription TEXT,
|
||||
seoKeywords TEXT,
|
||||
seoAuthor VARCHAR(255),
|
||||
seoOgImage VARCHAR(500),
|
||||
seoTwitterCard VARCHAR(100) DEFAULT 'summary_large_image',
|
||||
seoCanonicalUrl VARCHAR(500),
|
||||
seoRobots VARCHAR(100) DEFAULT 'index, follow',
|
||||
seoGoogleAnalytics VARCHAR(255),
|
||||
seoGoogleTagManager VARCHAR(255),
|
||||
seoFacebookPixel VARCHAR(255),
|
||||
settingCreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
settingModifiedDate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### File System Integration
|
||||
- **Upload Directory**: `public/uploads/site-settings/`
|
||||
- **Theme Directory**: `assets/style/css/`
|
||||
- **Backup Directory**: `backups/site-settings/`
|
||||
- **Permission Management**: Proper file system permissions
|
||||
- **Cleanup Procedures**: Automatic cleanup of unused files
|
||||
|
||||
### Global Integration Points
|
||||
|
||||
#### Header Component
|
||||
- **Logo Display**: Dynamic logo from site settings
|
||||
- **Site Name**: Configurable site name display
|
||||
- **Theme Selector**: Synchronized theme selection
|
||||
- **Brand Colors**: Applied from theme settings
|
||||
|
||||
#### App.vue Global Configuration
|
||||
- **Meta Tags**: Dynamically updated from settings
|
||||
- **Title Management**: Global title prefix from site name
|
||||
- **Theme Initialization**: Theme applied on app startup
|
||||
- **Favicon**: Dynamic favicon from settings
|
||||
|
||||
#### Loading Components
|
||||
- **Branded Loading**: Custom loading logo
|
||||
- **Brand Colors**: Theme colors in loading screens
|
||||
- **Consistent Experience**: Uniform branding across all loading states
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Branding Guidelines
|
||||
|
||||
#### Logo Design
|
||||
- **Vector Format**: Use SVG for scalability when possible
|
||||
- **Optimization**: Optimize file sizes for web performance
|
||||
- **Consistency**: Maintain consistent branding across all logos
|
||||
- **Accessibility**: Ensure sufficient contrast for accessibility compliance
|
||||
|
||||
#### Color Schemes
|
||||
- **Brand Consistency**: Align with organizational brand guidelines
|
||||
- **Accessibility**: Meet WCAG contrast requirements
|
||||
- **Cultural Sensitivity**: Consider cultural color associations
|
||||
- **Dark Mode Support**: Provide appropriate dark mode variants
|
||||
|
||||
### SEO Optimization
|
||||
|
||||
#### Content Strategy
|
||||
- **Unique Descriptions**: Unique meta descriptions for better SEO
|
||||
- **Keyword Research**: Use relevant keywords in meta tags
|
||||
- **Regular Updates**: Keep SEO content current and relevant
|
||||
- **Performance**: Optimize for page load speed
|
||||
|
||||
#### Technical SEO
|
||||
- **Structured Data**: Implement appropriate schema markup
|
||||
- **Mobile Optimization**: Ensure mobile-friendly configuration
|
||||
- **Page Speed**: Monitor and optimize loading times
|
||||
- **Security**: Implement HTTPS and security headers
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
#### File Optimization
|
||||
- **Image Compression**: Compress logos and images appropriately
|
||||
- **CSS Minification**: Minify custom CSS for better performance
|
||||
- **Caching Strategy**: Implement appropriate caching headers
|
||||
- **CDN Integration**: Consider CDN for static assets
|
||||
|
||||
#### Theme Performance
|
||||
- **Minimal CSS**: Keep custom themes lightweight
|
||||
- **Variable Usage**: Use CSS custom properties efficiently
|
||||
- **Browser Compatibility**: Test across different browsers
|
||||
- **Fallback Support**: Provide fallbacks for older browsers
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
#### File Upload Security
|
||||
- **File Type Validation**: Strict file type checking
|
||||
- **Size Limits**: Appropriate file size restrictions
|
||||
- **Virus Scanning**: Implement virus scanning for uploads
|
||||
- **Path Traversal**: Prevent directory traversal attacks
|
||||
|
||||
#### CSS Security
|
||||
- **Content Filtering**: Filter potentially dangerous CSS
|
||||
- **XSS Prevention**: Prevent CSS-based XSS attacks
|
||||
- **Input Validation**: Validate all CSS input thoroughly
|
||||
- **Sanitization**: Sanitize user-provided CSS content
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Settings Not Saving
|
||||
**Symptoms**:
|
||||
- Changes revert after page refresh
|
||||
- Error messages during save operations
|
||||
- Loading indicators that don't complete
|
||||
|
||||
**Resolution Steps**:
|
||||
1. **Check Permissions**: Verify administrative access
|
||||
2. **Network Issues**: Confirm stable internet connection
|
||||
3. **Browser Console**: Check for JavaScript errors
|
||||
4. **Server Status**: Verify server is operational
|
||||
5. **Database Connection**: Check database connectivity
|
||||
|
||||
#### Logos Not Displaying
|
||||
**Symptoms**:
|
||||
- Broken image icons instead of logos
|
||||
- Default logos showing instead of custom ones
|
||||
- Images not loading properly
|
||||
|
||||
**Resolution Steps**:
|
||||
1. **File Path**: Verify correct file paths in settings
|
||||
2. **File Permissions**: Check server file permissions
|
||||
3. **File Format**: Ensure supported image formats
|
||||
4. **File Size**: Check if files exceed size limits
|
||||
5. **Browser Cache**: Clear browser cache and reload
|
||||
|
||||
#### Theme Not Applying
|
||||
**Symptoms**:
|
||||
- Theme selection not taking effect
|
||||
- Colors not changing as expected
|
||||
- Custom themes not available
|
||||
|
||||
**Resolution Steps**:
|
||||
1. **CSS Validation**: Check for CSS syntax errors
|
||||
2. **Theme File**: Verify theme file integration
|
||||
3. **Browser Compatibility**: Test with different browsers
|
||||
4. **Cache Issues**: Clear browser and server cache
|
||||
5. **CSS Conflicts**: Check for CSS specificity issues
|
||||
|
||||
### Performance Issues
|
||||
|
||||
#### Slow Loading
|
||||
**Causes**:
|
||||
- Large logo files
|
||||
- Excessive custom CSS
|
||||
- Network connectivity issues
|
||||
- Server performance problems
|
||||
|
||||
**Optimization**:
|
||||
1. **Image Optimization**: Compress logo files
|
||||
2. **CSS Optimization**: Minimize custom CSS
|
||||
3. **Caching**: Implement proper caching strategies
|
||||
4. **CDN**: Use content delivery networks
|
||||
5. **Server Optimization**: Monitor server performance
|
||||
|
||||
#### Memory Issues
|
||||
**Symptoms**:
|
||||
- Browser becoming unresponsive
|
||||
- High memory usage
|
||||
- Slow interface response
|
||||
|
||||
**Resolution**:
|
||||
1. **File Size**: Reduce logo file sizes
|
||||
2. **CSS Efficiency**: Optimize CSS selectors
|
||||
3. **Browser Update**: Use latest browser versions
|
||||
4. **System Resources**: Monitor system memory
|
||||
5. **Cache Management**: Regular cache clearing
|
||||
|
||||
### Support Resources
|
||||
|
||||
#### Documentation
|
||||
- **Technical Guide**: Comprehensive system documentation
|
||||
- **User Guide**: End-user documentation
|
||||
- **API Documentation**: Developer integration guides
|
||||
- **Change Log**: System updates and changes
|
||||
|
||||
#### Contact Support
|
||||
- **Internal IT**: Contact system administrators
|
||||
- **Help Desk**: Organizational support procedures
|
||||
- **Community**: User forums and knowledge bases
|
||||
- **Vendor Support**: External support options
|
||||
|
||||
#### Information to Provide
|
||||
When reporting issues:
|
||||
- **Error Messages**: Exact error text
|
||||
- **Browser Information**: Browser type and version
|
||||
- **Steps to Reproduce**: Detailed reproduction steps
|
||||
- **Screenshots**: Visual documentation of issues
|
||||
- **System Details**: Relevant configuration information
|
||||
|
||||
This comprehensive guide provides complete coverage of the Site Settings system, enabling administrators to effectively customize and manage the visual appearance and global configuration of the EDMS application.
|
File diff suppressed because it is too large
Load Diff
@ -4,13 +4,13 @@
|
||||
1. [Introduction](#introduction)
|
||||
2. [Getting Started](#getting-started)
|
||||
3. [Navigation](#navigation)
|
||||
4. [Document Organization](#document-organization)
|
||||
5. [Working with Documents](#working-with-documents)
|
||||
6. [Access Control](#access-control)
|
||||
7. [Search and Filtering](#search-and-filtering)
|
||||
8. [Document Viewer](#document-viewer)
|
||||
9. [My Documents](#my-documents)
|
||||
10. [Settings Administration](#settings-administration)
|
||||
4. [DMS Interface](#dms-interface)
|
||||
5. [Access Level System](#access-level-system)
|
||||
6. [Document Organization](#document-organization)
|
||||
7. [Working with Documents](#working-with-documents)
|
||||
8. [DMS Settings (Administrators)](#dms-settings-administrators)
|
||||
9. [Search and Filtering](#search-and-filtering)
|
||||
10. [Document Viewer](#document-viewer)
|
||||
11. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Introduction
|
||||
@ -18,9 +18,10 @@
|
||||
The Electronic Document Management System (EDMS) is a modern web-based platform designed to efficiently organize, store, and manage digital documents in organizational environments. The system provides a secure, hierarchical structure for document storage with comprehensive role-based access control and advanced document management capabilities.
|
||||
|
||||
### Key Features
|
||||
- **Hierarchical Organization**: Documents are organized in a Cabinet → Drawer → Folder → Subfolder structure
|
||||
- **Access Level Organization**: Documents categorized as All, Public, Private, and Personal with color-coded tabs
|
||||
- **Hierarchical Structure**: Documents organized in a Cabinet → Drawer → Folder → Subfolder structure
|
||||
- **Multiple View Modes**: List, Grid, and Details views for browsing documents
|
||||
- **Access Control**: Public, Private, and Personal document categories with granular permission management
|
||||
- **Advanced Settings Management**: Comprehensive configuration system for administrators
|
||||
- **Document Viewer**: Built-in viewer supporting multiple file formats (PDF, images, text files, spreadsheets)
|
||||
- **Search Functionality**: Advanced search across document titles, descriptions, and metadata
|
||||
- **Upload Management**: Drag-and-drop file upload with comprehensive metadata assignment
|
||||
@ -55,27 +56,32 @@ The main dashboard provides:
|
||||
### Main Navigation Menu
|
||||
Access the EDMS through the main navigation menu:
|
||||
- **Dashboard**: System overview and quick access to recent items
|
||||
- **DMS**: Main document management interface
|
||||
- **My Documents**: Personal document collection and workspace
|
||||
- **Upload Document**: Dedicated document upload interface
|
||||
- **DMS**: Main document management interface with access level tabs
|
||||
- **DMS Settings**: Administrative configuration panel (admin only)
|
||||
- **ERD**: Entity Relationship Diagram viewer for system structure
|
||||
|
||||
### EDMS Interface Layout
|
||||
The EDMS interface consists of three main areas:
|
||||
The EDMS interface consists of several main areas:
|
||||
|
||||
1. **Tree Navigation (Left Panel)**
|
||||
1. **Access Level Tabs (Top)**
|
||||
- **All Documents**: Complete view of all accessible documents (blue folder icon)
|
||||
- **Public**: Documents available to all users (green unlock icon)
|
||||
- **Private**: Restricted documents requiring special permissions (red lock icon)
|
||||
- **Personal**: Individual user documents (purple user icon)
|
||||
|
||||
2. **Tree Navigation (Left Panel - when available)**
|
||||
- Hierarchical view of cabinets, drawers, and folders
|
||||
- Expandable/collapsible folder structure
|
||||
- Color-coded access indicators for different permission levels
|
||||
- Lock icons for restricted items requiring special access
|
||||
|
||||
2. **Content Area (Center)**
|
||||
3. **Content Area (Center)**
|
||||
- Document listing with multiple view modes (List, Grid, Details)
|
||||
- Breadcrumb navigation showing current location
|
||||
- Search and filter tools for finding documents
|
||||
- View mode controls and sorting options
|
||||
- Document tabs for filtering by access type
|
||||
|
||||
3. **Details Panel (Optional Right Panel)**
|
||||
4. **Details Panel (Optional Right Panel)**
|
||||
- Document properties and metadata information
|
||||
- Quick actions and document tools
|
||||
- Access control information and permissions
|
||||
@ -87,6 +93,82 @@ The EDMS interface consists of three main areas:
|
||||
- Helps maintain orientation in deep folder structures
|
||||
- Displays full path from root to current location
|
||||
|
||||
## DMS Interface
|
||||
|
||||
### Access Level Tabs
|
||||
The main DMS interface features a modern tabbed design for easy navigation between different document categories:
|
||||
|
||||
#### All Documents Tab
|
||||
- **Icon**: Blue folder icon
|
||||
- **Purpose**: Shows all documents you have permission to access
|
||||
- **Color Scheme**: Blue theme with active indicator
|
||||
- **Use Case**: Comprehensive view for finding any accessible document
|
||||
|
||||
#### Public Tab
|
||||
- **Icon**: Green unlock icon
|
||||
- **Purpose**: Displays documents accessible to all system users
|
||||
- **Color Scheme**: Green theme indicating open access
|
||||
- **Use Case**: Company policies, public announcements, shared resources
|
||||
|
||||
#### Private Tab
|
||||
- **Icon**: Red lock icon
|
||||
- **Purpose**: Shows restricted documents requiring special permissions
|
||||
- **Color Scheme**: Red theme indicating restricted access
|
||||
- **Use Case**: Confidential documents, sensitive information, executive files
|
||||
|
||||
#### Personal Tab
|
||||
- **Icon**: Purple user icon
|
||||
- **Purpose**: Displays your personal documents and private workspace
|
||||
- **Color Scheme**: Purple theme indicating personal ownership
|
||||
- **Use Case**: Individual workspace, draft documents, personal files
|
||||
|
||||
### Visual Design Features
|
||||
- **Active Tab Indicators**: Current tab highlighted with "Active" badge
|
||||
- **Hover Effects**: Smooth transitions when hovering over tabs
|
||||
- **Loading States**: Visual feedback during system operations
|
||||
- **Error Handling**: Clear error messages with retry options
|
||||
|
||||
## Access Level System
|
||||
|
||||
### Understanding Access Levels
|
||||
|
||||
#### Public Documents
|
||||
- **Accessibility**: Visible and accessible to all system users
|
||||
- **Visual Indicator**: Green color theme with unlock icon
|
||||
- **Permissions**: No special permissions or approvals required
|
||||
- **Best For**:
|
||||
- Company policies and procedures
|
||||
- Public announcements
|
||||
- Shared templates and forms
|
||||
- General information documents
|
||||
|
||||
#### Private Documents
|
||||
- **Accessibility**: Restricted access requiring specific permissions
|
||||
- **Visual Indicator**: Red color theme with lock icon
|
||||
- **Permissions**: May require access request and approval workflow
|
||||
- **Best For**:
|
||||
- Confidential business information
|
||||
- HR records and sensitive data
|
||||
- Financial documents
|
||||
- Executive communications
|
||||
- Legal documents
|
||||
|
||||
#### Personal Documents
|
||||
- **Accessibility**: Documents owned and controlled by individual users
|
||||
- **Visual Indicator**: Purple color theme with user icon
|
||||
- **Permissions**: Full control over sharing and access
|
||||
- **Best For**:
|
||||
- Individual workspace documents
|
||||
- Draft documents before publication
|
||||
- Personal notes and references
|
||||
- Work-in-progress files
|
||||
|
||||
### Access Level Switching
|
||||
- Click any access level tab to filter documents by that category
|
||||
- Visual feedback shows which tab is currently active
|
||||
- Document count may vary between tabs based on your permissions
|
||||
- Some tabs may be empty if no documents exist in that category
|
||||
|
||||
## Document Organization
|
||||
|
||||
### Hierarchical Structure
|
||||
@ -106,8 +188,9 @@ Documents are organized in a four-level hierarchy designed for maximum flexibili
|
||||
### Visual Indicators
|
||||
The system uses color coding and icons to indicate access levels:
|
||||
- **Green**: Public documents (open access for all users)
|
||||
- **Blue**: Personal documents (owned by you)
|
||||
- **Blue**: All accessible documents (comprehensive view)
|
||||
- **Red**: Private documents (restricted access)
|
||||
- **Purple**: Personal documents (owned by you)
|
||||
- **Lock Icon**: Documents you cannot currently access
|
||||
- **Template Icon**: Document templates available for use
|
||||
|
||||
@ -117,7 +200,7 @@ The system uses color coding and icons to indicate access levels:
|
||||
|
||||
#### Single File Upload
|
||||
1. Navigate to the desired location in the hierarchy
|
||||
2. Click the "Upload" button or use the dedicated "Upload Document" page
|
||||
2. Click the "Upload" button or use the dedicated upload interface
|
||||
3. Select file(s) using the file picker or drag-and-drop interface
|
||||
4. Fill in comprehensive document metadata:
|
||||
- Document title and description
|
||||
@ -136,16 +219,17 @@ The system uses color coding and icons to indicate access levels:
|
||||
5. Review and confirm successful uploads
|
||||
|
||||
### Document Metadata
|
||||
For each document, you can specify comprehensive metadata:
|
||||
For each document, you can specify comprehensive metadata based on system configuration:
|
||||
- **Title**: Descriptive document name/title
|
||||
- **Description**: Brief summary of document content and purpose
|
||||
- **Keywords**: Search terms for improved discoverability (comma-separated)
|
||||
- **Category**: Document type (Technical Specification, Contract, Report, Policy, etc.)
|
||||
- **Category**: Document type (configurable in DMS settings)
|
||||
- **Status**: Current state (Draft, Under Review, Approved, Archived, etc.)
|
||||
- **Department**: Responsible department or team
|
||||
- **Access Level**: Determines who can view and interact with the document
|
||||
- **Retention Period**: How long the document should be retained
|
||||
- **Version**: Document version number and change notes
|
||||
- **Custom Fields**: Additional metadata fields as configured by administrators
|
||||
|
||||
### Viewing Documents
|
||||
1. Click on any document to view its details and metadata
|
||||
@ -158,392 +242,218 @@ For each document, you can specify comprehensive metadata:
|
||||
- **Move Documents**: Drag and drop to reorganize into different locations
|
||||
- **Delete Documents**: Remove documents with confirmation and audit trail
|
||||
- **Copy Documents**: Duplicate documents to multiple locations
|
||||
- **Version Control**: Track document versions and view change history
|
||||
- **Version Control**: Track document versions and view change history (if enabled)
|
||||
- **Share Documents**: Generate sharing links and manage access permissions
|
||||
|
||||
## Access Control
|
||||
|
||||
### Understanding Access Levels
|
||||
|
||||
#### Public Documents
|
||||
- Visible and accessible to all system users
|
||||
- No special permissions or approvals required
|
||||
- Green color indicator for easy identification
|
||||
- Suitable for general information and policies
|
||||
|
||||
#### Private Documents
|
||||
- Restricted access requiring specific permissions
|
||||
- Red color indicator with lock icon
|
||||
- May require access request and approval workflow
|
||||
- Ideal for sensitive or confidential information
|
||||
|
||||
#### Personal Documents
|
||||
- Documents owned and controlled by individual users
|
||||
- Blue color indicator showing ownership
|
||||
- Full control over sharing and permissions
|
||||
- Private workspace for individual document management
|
||||
|
||||
### Requesting Access
|
||||
When you encounter a private document you cannot access:
|
||||
|
||||
1. **Identify Restricted Item**: Look for red color coding and lock icon
|
||||
2. **Initiate Access Request**: Click the "Request Access" button
|
||||
3. **Complete Request Form**:
|
||||
- **Document Information**: Pre-filled with document title and file details
|
||||
- **Access Type**: Choose appropriate permission level:
|
||||
- View Only: Read-only access to document content
|
||||
- Download: View and download permissions for offline access
|
||||
- Print: View and print permissions for physical copies
|
||||
- Full Access: Complete access rights including editing
|
||||
- **Access Duration**: Select appropriate timeframe:
|
||||
- 7 days, 14 days, 30 days, 60 days, 90 days, or Permanent
|
||||
- **Justification**: Detailed explanation of why access is needed (required field)
|
||||
4. **Submit Request**: Click "Submit Request" to send for approval
|
||||
5. **Monitor Status**: Track request status and wait for approval from document owner or administrator
|
||||
|
||||
### Access Request Status
|
||||
- **Pending**: Request submitted and awaiting review by approvers
|
||||
- **Approved**: Access granted for specified duration and permission level
|
||||
- **Rejected**: Request denied with explanation from approver
|
||||
- **Expired**: Previously approved access has reached its expiration date
|
||||
|
||||
## Search and Filtering
|
||||
|
||||
### Basic Search
|
||||
1. Use the search box in the top navigation bar
|
||||
2. Enter keywords, document titles, or content descriptions
|
||||
3. Press Enter or click the search icon to execute search
|
||||
4. Results display with matching documents and highlighted search terms
|
||||
5. Refine results using additional filters and sorting options
|
||||
|
||||
### Advanced Search Options
|
||||
- **Filter by Type**: Documents, folders, cabinets, or specific file formats
|
||||
- **Filter by Category**: Technical, Administrative, Financial, HR, etc.
|
||||
- **Filter by Date Range**: Creation date, modification date, or access date
|
||||
- **Filter by Owner**: Documents by specific users or departments
|
||||
- **Filter by Access Level**: Public, private, personal, or template documents
|
||||
- **Filter by Status**: Draft, approved, archived, or under review
|
||||
- **Filter by File Format**: PDF, Word, Excel, images, etc.
|
||||
|
||||
### Search Tips for Better Results
|
||||
- Use quotation marks for exact phrase matching
|
||||
- Combine multiple keywords with spaces for broader results
|
||||
- Search includes document titles, descriptions, metadata, and content (where supported)
|
||||
- Use wildcards (*) for partial word matching
|
||||
- Utilize Boolean operators (AND, OR, NOT) for complex searches
|
||||
|
||||
### Document Tabs for Quick Filtering
|
||||
Filter documents by category using convenient tabs:
|
||||
- **All**: Display all accessible documents in current location
|
||||
- **Public**: Show only public documents available to all users
|
||||
- **Private**: Display private documents you have access to
|
||||
- **Personal**: Show your personal documents and workspace items
|
||||
|
||||
## Document Viewer
|
||||
|
||||
### Supported File Formats
|
||||
The built-in document viewer supports multiple formats:
|
||||
- **PDF Documents**: Full preview with zoom, navigation, and text selection
|
||||
- **Images**: JPG, PNG, GIF, BMP with zoom and pan functionality
|
||||
- **Text Files**: TXT, RTF with formatted preview and search
|
||||
- **Microsoft Office**: DOC, DOCX, XLS, XLSX, PPT, PPTX (basic preview)
|
||||
- **Other Formats**: Download option provided for unsupported formats
|
||||
|
||||
### Viewer Features and Controls
|
||||
- **Zoom Controls**: Zoom in/out from 25% to 400% for optimal readability
|
||||
- **Page Navigation**: Navigate through multi-page documents with thumbnails
|
||||
- **Full Screen Mode**: Maximize viewer for immersive document experience
|
||||
- **Download Function**: Save document locally for offline access
|
||||
- **Print Support**: Print document directly from viewer interface
|
||||
- **Search in Document**: Find specific text within document content
|
||||
- **Rotation Controls**: Rotate document pages for better viewing
|
||||
|
||||
### Viewer Interface Controls
|
||||
- **Zoom Options**: 25%, 50%, 75%, 100%, 125%, 150%, 200%, Fit to Width, Fit to Page
|
||||
- **Navigation**: Previous/Next page buttons, page number input, thumbnail sidebar
|
||||
- **View Options**: Single page, continuous scroll, two-page spread
|
||||
- **Tools**: Text selection, annotation tools (if enabled), measurement tools
|
||||
- **Close**: Return to document list or previous view
|
||||
|
||||
## My Documents
|
||||
|
||||
### Personal Document Workspace
|
||||
The "My Documents" section provides a personalized workspace showing:
|
||||
- Documents you've created and own
|
||||
- Documents you've recently accessed or modified
|
||||
- Documents shared with you by other users
|
||||
- Recent document activity and version history
|
||||
- Personal folders and organizational structure
|
||||
|
||||
### Organization and Management Features
|
||||
- **Quick Search**: Search specifically within your personal documents
|
||||
- **Sort Options**: Sort by name, date created, date modified, file type, or size
|
||||
- **View Modes**: Switch between list, grid, or detailed view layouts
|
||||
- **Bulk Actions**: Select and manage multiple documents simultaneously
|
||||
- **Folder Creation**: Create personal folders for document organization
|
||||
- **Favorites**: Mark frequently used documents for quick access
|
||||
|
||||
### Personal Document Statistics and Insights
|
||||
View comprehensive information about your document usage:
|
||||
- Total number of documents owned and accessible
|
||||
- Storage space used and available quota
|
||||
- Recent upload activity and document creation trends
|
||||
- Document categories and type distribution
|
||||
- Most accessed documents and usage patterns
|
||||
|
||||
## Settings Administration
|
||||
## DMS Settings (Administrators)
|
||||
|
||||
### Overview
|
||||
The EDMS Settings module provides administrators with comprehensive control over system configuration and behavior. Access to settings is restricted to users with administrative privileges and affects system-wide operations.
|
||||
Administrators have access to comprehensive DMS settings through the **DMS Settings** page, allowing complete customization of the document management system behavior.
|
||||
|
||||
### Accessing Settings
|
||||
1. Navigate to **DMS** → **Settings** from the main navigation menu
|
||||
2. Select a settings category from the left sidebar navigation
|
||||
3. Configure settings within each category using standardized form components
|
||||
4. Save changes to apply configurations across the system
|
||||
### Accessing DMS Settings
|
||||
1. Navigate to **DMS** → **Settings** in the main menu
|
||||
2. Requires administrative privileges to access
|
||||
3. Settings are organized into six main categories with tabbed navigation
|
||||
|
||||
### Settings Categories
|
||||
|
||||
The EDMS settings are organized into 5 core categories for streamlined administration:
|
||||
#### 1. User & Access Management 🔐
|
||||
- **User Roles**: Define and manage custom user roles (Admin, Editor, Viewer, Uploader, etc.)
|
||||
- **Access Permissions**: Configure granular permissions for view, edit, delete, download, and share
|
||||
- **Authentication Settings**:
|
||||
- Enable/disable Single Sign-On (SSO)
|
||||
- Require Multi-Factor Authentication (MFA)
|
||||
- LDAP/Active Directory integration
|
||||
- Session timeout configuration (1-24 hours)
|
||||
|
||||
#### 🔐 User & Access Management
|
||||
Configure user permissions, roles, and authentication settings:
|
||||
#### 2. Document & Folder Settings 📁
|
||||
- **Naming Conventions**:
|
||||
- Auto-generate document names
|
||||
- Configure naming patterns (e.g., `{department}_{title}_{date}`)
|
||||
- Set mandatory fields for document metadata
|
||||
- **Version Control**:
|
||||
- Enable/disable document versioning
|
||||
- Set maximum number of versions to retain
|
||||
- Configure automatic versioning behavior
|
||||
|
||||
**User Roles & Permissions**
|
||||
- Define user roles (Admin, Editor, Viewer, Uploader)
|
||||
- Set granular permissions for each role:
|
||||
- View Documents: Read-only access to document content
|
||||
- Edit Documents: Modify document metadata and properties
|
||||
- Delete Documents: Remove documents with audit trail
|
||||
- Download Documents: Save documents for offline access
|
||||
- Share Documents: Generate sharing links and manage access
|
||||
- Manage user groups by department or project
|
||||
- Enable Role-Based Access Control (RBAC) system-wide
|
||||
#### 3. Metadata & Tagging 📝
|
||||
- **Custom Metadata Fields**:
|
||||
- Add/remove custom fields (text, dropdown, date, number, multi-select)
|
||||
- Set required fields for document uploads
|
||||
- Configure field types and validation rules
|
||||
- **Tagging System**:
|
||||
- Manage predefined tags (urgent, confidential, public, draft, final, etc.)
|
||||
- Enable/disable user-generated tags
|
||||
- Configure tag suggestions and auto-completion
|
||||
|
||||
**Authentication Settings**
|
||||
- Single Sign-On (SSO) integration with external providers
|
||||
- Multi-Factor Authentication (MFA) enforcement
|
||||
- LDAP/Active Directory integration for enterprise environments
|
||||
- Session timeout configuration (1-24 hours)
|
||||
- Password policies and security requirements
|
||||
#### 4. Workflow & Automation 🔄
|
||||
- **Approval Workflows**:
|
||||
- Enable/disable approval workflows
|
||||
- Configure default approval flow (department-head-approval, legal-review, etc.)
|
||||
- Set up custom approval processes
|
||||
- **Notifications**:
|
||||
- Email notifications for document activities
|
||||
- In-app notifications and alerts
|
||||
- Upload alerts and deadline reminders
|
||||
|
||||
#### 📁 Document & Folder Settings
|
||||
Configure document organization and structure:
|
||||
#### 5. Upload & Storage Settings 📤
|
||||
- **File Type Management**:
|
||||
- Allowed file types (pdf, doc, docx, xls, xlsx, ppt, pptx, txt, jpg, png, etc.)
|
||||
- Blocked file types (exe, bat, cmd, etc.)
|
||||
- Configure MIME type validation
|
||||
- **Storage Quotas**:
|
||||
- Maximum file size limit (MB)
|
||||
- Per-user storage quota
|
||||
- Per-group storage quota
|
||||
- Per-project storage quota
|
||||
|
||||
**Folder Hierarchy**
|
||||
- Set maximum folder depth (1-10 levels)
|
||||
- Choose default folder structure templates:
|
||||
- Standard: General-purpose organization
|
||||
- Project-based: Project-focused hierarchy
|
||||
- Department-based: Organizational structure alignment
|
||||
- Custom: User-defined structure
|
||||
- Configure folder creation permissions and restrictions
|
||||
|
||||
**Document Naming Conventions**
|
||||
- Enable automatic document name generation
|
||||
- Define mandatory metadata fields for document creation
|
||||
- Set naming patterns using variables:
|
||||
- `{department}`: Document department/owner
|
||||
- `{title}`: Document title or name
|
||||
- `{date}`: Creation or upload date
|
||||
- `{project}`: Project code or identifier
|
||||
- `{category}`: Document category/type
|
||||
|
||||
**Document Retention & Version Control**
|
||||
- Enable document retention policies with configurable retention periods
|
||||
- Set automatic archiving before deletion
|
||||
- Configure version control system:
|
||||
- Maximum versions to retain (1-50)
|
||||
- Automatic versioning on document updates
|
||||
- Version history tracking and comparison tools
|
||||
|
||||
#### 📝 Metadata & Tagging
|
||||
Configure document metadata and classification:
|
||||
|
||||
**Custom Metadata Fields**
|
||||
Define custom fields for enhanced document organization:
|
||||
- Field types: Text, Dropdown, Date, Number, Multi-select
|
||||
- Required field enforcement for document uploads
|
||||
- Department-specific metadata schemas
|
||||
- Dynamic field validation and formatting rules
|
||||
|
||||
**Predefined Metadata Fields**
|
||||
- Department: Organizational unit assignment
|
||||
- Priority: Document importance level (Low, Medium, High, Critical)
|
||||
- Project Code: Project or initiative identifier
|
||||
- Review Date: Scheduled review or expiration date
|
||||
|
||||
**Tagging System**
|
||||
- Configure predefined tags: urgent, confidential, public, draft, final
|
||||
- Enable user-generated tags for flexible categorization
|
||||
- Tag suggestion system for consistent tagging
|
||||
- Auto-classification based on:
|
||||
- Keyword recognition in document content
|
||||
- Department-based classification rules
|
||||
- File type and format analysis
|
||||
|
||||
#### 📤 Upload & Storage Settings
|
||||
Configure file upload restrictions and storage management:
|
||||
|
||||
**File Type Management**
|
||||
- **Allowed File Types**: Specify permitted file formats
|
||||
- Default: pdf, doc, docx, xls, xlsx, ppt, pptx, txt, jpg, png
|
||||
- Configurable list for organizational requirements
|
||||
- **Blocked File Types**: Prevent potentially harmful files
|
||||
- Default: exe, bat, cmd (executable files)
|
||||
- Customizable blacklist for security compliance
|
||||
|
||||
**File Size & Storage Quotas**
|
||||
- Maximum file size per upload (typically 100MB)
|
||||
- User storage quotas (default: 5GB per user)
|
||||
- Group storage quotas (default: 50GB per group)
|
||||
- Project storage quotas (default: 100GB per project)
|
||||
- System-wide storage monitoring and alerts
|
||||
|
||||
**Storage Configuration**
|
||||
- Storage type selection (Local, AWS S3, Azure, Google Cloud)
|
||||
- Storage path configuration for file organization
|
||||
- Backup settings and automated backup scheduling
|
||||
- Compression settings for storage optimization
|
||||
|
||||
#### 📅 System Settings
|
||||
Configure global system behavior and preferences:
|
||||
|
||||
**General System Configuration**
|
||||
- **System Timezone**: Set default timezone for timestamps
|
||||
- Options: Asia/Kuala_Lumpur, UTC, America/New_York, Europe/London
|
||||
- Affects document timestamps, scheduling, and user interface
|
||||
- **Backup Schedule**: Configure automated system backups
|
||||
- Options: Hourly, Daily, Weekly, Monthly
|
||||
- Affects data protection and recovery capabilities
|
||||
- **Log Level**: Set system logging verbosity
|
||||
- Options: Debug, Info, Warning, Error
|
||||
- Affects troubleshooting and system monitoring
|
||||
|
||||
**System Monitoring & Maintenance**
|
||||
- **Maintenance Mode**: Enable system-wide maintenance mode
|
||||
- Displays maintenance message to users
|
||||
- Restricts access during system updates
|
||||
- **Automatic Updates**: Configure automatic system updates
|
||||
- Security patches and feature updates
|
||||
- Scheduled during low-usage periods
|
||||
- **System Monitoring**: Enable performance monitoring
|
||||
- Track system performance metrics
|
||||
- Monitor user activity and system health
|
||||
- Generate usage reports and analytics
|
||||
#### 6. System Settings 📅
|
||||
- **General Configuration**:
|
||||
- System timezone (Asia/Kuala_Lumpur, UTC, etc.)
|
||||
- Backup schedule (hourly, daily, weekly, monthly)
|
||||
- System log level (debug, info, warning, error)
|
||||
- **System Maintenance**:
|
||||
- Enable/disable maintenance mode
|
||||
- Configure automatic updates
|
||||
- System monitoring and performance metrics
|
||||
|
||||
### Settings Management Features
|
||||
|
||||
#### Import/Export Configuration
|
||||
- **Export Settings**: Save current configuration as JSON file
|
||||
- Backup settings before major changes
|
||||
- Transfer settings between environments
|
||||
- Documentation and compliance purposes
|
||||
- **Import Settings**: Load configuration from JSON file
|
||||
- Restore previous configurations
|
||||
- Deploy settings across multiple systems
|
||||
- Standardize configurations across environments
|
||||
#### Save & Load Settings
|
||||
- **Auto-save**: Settings are saved immediately when modified
|
||||
- **Loading States**: Visual feedback during save operations
|
||||
- **Error Handling**: Clear error messages if save fails
|
||||
- **Success Confirmation**: Confirmation messages with auto-dismiss
|
||||
|
||||
#### Import/Export Functionality
|
||||
- **Export Settings**: Download complete settings as JSON file for backup
|
||||
- **Import Settings**: Upload JSON file to restore settings
|
||||
- **Settings Migration**: Easy transfer between environments
|
||||
- **Backup Management**: Regular backups of configuration
|
||||
|
||||
#### Reset to Defaults
|
||||
- **Category Reset**: Reset individual setting categories to default values
|
||||
- **System Reset**: Reset entire system configuration to factory defaults
|
||||
- **Selective Reset**: Choose specific settings to reset while preserving others
|
||||
- **One-click Reset**: Reset all settings to system defaults
|
||||
- **Confirmation Dialog**: Prevents accidental resets
|
||||
- **Selective Reset**: Reset individual setting categories (future enhancement)
|
||||
- **Default Values**: Sensible defaults for all configuration options
|
||||
|
||||
#### Settings Validation
|
||||
- **Real-time Validation**: Form validation prevents invalid configurations
|
||||
- **Dependency Checking**: Settings are validated for interdependencies
|
||||
- **Conflict Resolution**: System identifies and resolves setting conflicts
|
||||
- **Preview Mode**: Test settings before applying system-wide
|
||||
#### Dynamic Field Management
|
||||
- **Add Custom Fields**: Create new metadata fields with validation
|
||||
- **Remove Fields**: Delete unused custom fields
|
||||
- **Reorder Fields**: Arrange custom fields in preferred order
|
||||
- **Field Types**: Support for text, dropdown, date, number, and multi-select fields
|
||||
|
||||
### Best Practices for Settings Management
|
||||
### Configuration Best Practices
|
||||
|
||||
#### Security Considerations
|
||||
- Regularly review user permissions and access levels
|
||||
- Enable MFA for administrative accounts
|
||||
- Use strong session timeout values for security
|
||||
- Regularly audit user roles and group memberships
|
||||
#### Security Settings
|
||||
- Enable MFA for sensitive environments
|
||||
- Configure appropriate session timeouts
|
||||
- Set up proper user roles and permissions
|
||||
- Regularly review access control settings
|
||||
|
||||
#### Storage Management
|
||||
- Monitor storage quotas and usage patterns
|
||||
- Implement retention policies for compliance
|
||||
- Regular backup verification and testing
|
||||
- Plan for storage scaling based on usage growth
|
||||
- Set realistic file size limits based on storage capacity
|
||||
- Configure appropriate quotas for users and groups
|
||||
- Enable compression for large files when possible
|
||||
- Set up regular backup schedules
|
||||
|
||||
#### System Performance
|
||||
- Monitor log levels to balance debugging needs with performance
|
||||
- Schedule maintenance during off-peak hours
|
||||
- Regular cleanup of temporary files and old versions
|
||||
- Performance monitoring for system optimization
|
||||
#### Workflow Optimization
|
||||
- Configure approval workflows based on organizational structure
|
||||
- Enable relevant notifications to keep users informed
|
||||
- Set up appropriate retention policies
|
||||
- Configure automatic tagging rules for efficiency
|
||||
|
||||
#### Documentation & Compliance
|
||||
- Document all configuration changes with justification
|
||||
- Maintain change logs for audit purposes
|
||||
- Regular export of settings for backup and documentation
|
||||
- Review settings for compliance with organizational policies
|
||||
## Search and Filtering
|
||||
|
||||
### Troubleshooting Settings Issues
|
||||
### Search Functionality
|
||||
- **Global Search**: Search across all accessible documents
|
||||
- **Tab-Specific Search**: Search within specific access level tabs
|
||||
- **Keyword Search**: Search document titles, descriptions, and metadata
|
||||
- **Advanced Filters**: Filter by date, document type, department, etc.
|
||||
|
||||
#### Common Settings Problems
|
||||
- **Permission Conflicts**: Resolve conflicting role assignments
|
||||
- **Upload Failures**: Check file type restrictions and size limits
|
||||
- **Authentication Issues**: Verify SSO and authentication settings
|
||||
- **Storage Problems**: Monitor quotas and storage allocation
|
||||
### Search Tips
|
||||
- Use quotation marks for exact phrase searches
|
||||
- Combine multiple keywords for more specific results
|
||||
- Use the predefined tags for quick filtering
|
||||
- Take advantage of custom metadata fields for precise searches
|
||||
|
||||
#### Settings Recovery
|
||||
- Use exported settings files to restore configurations
|
||||
- Reset specific categories if issues persist
|
||||
- Contact system administrator for complex configuration issues
|
||||
- Review audit logs for recent configuration changes
|
||||
## Document Viewer
|
||||
|
||||
### Supported File Types
|
||||
The built-in document viewer supports:
|
||||
- **PDF Documents**: Full PDF viewing with zoom and navigation
|
||||
- **Microsoft Office**: Word, Excel, PowerPoint documents
|
||||
- **Images**: JPG, PNG, GIF, and other common image formats
|
||||
- **Text Files**: Plain text, CSV, and other text-based formats
|
||||
|
||||
### Viewer Features
|
||||
- **Zoom Controls**: Zoom in/out for better readability
|
||||
- **Page Navigation**: Navigate through multi-page documents
|
||||
- **Full Screen Mode**: Distraction-free document viewing
|
||||
- **Download Option**: Download documents for offline access
|
||||
- **Print Support**: Direct printing from the viewer
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues and Solutions
|
||||
### Common Issues
|
||||
|
||||
#### Cannot Access Document
|
||||
- **Check Access Level**: Verify document permissions and look for lock icons
|
||||
- **Request Access**: Use the access request feature for private documents
|
||||
- **Contact Administrator**: Reach out for urgent access needs or system issues
|
||||
- **Verify Account Status**: Ensure your user account is active and properly configured
|
||||
#### Documents Not Loading
|
||||
1. Check your internet connection
|
||||
2. Verify you have permission to access the document
|
||||
3. Try refreshing the page or switching tabs
|
||||
4. Contact administrator if the issue persists
|
||||
|
||||
#### Upload Problems
|
||||
- **File Size Limits**: Check maximum file size restrictions (typically 50MB)
|
||||
- **File Format Support**: Ensure file format is supported by the system
|
||||
- **Network Connection**: Verify stable internet connection for large uploads
|
||||
- **Upload Permissions**: Confirm you have upload permissions for the target location
|
||||
- **Browser Issues**: Try clearing cache or using a different browser
|
||||
#### Upload Failures
|
||||
1. Check file size limits (configured in DMS settings)
|
||||
2. Verify file type is allowed (check blocked file types in settings)
|
||||
3. Ensure you have upload permissions in current location
|
||||
4. Try uploading one file at a time if bulk upload fails
|
||||
|
||||
#### Search Not Working Properly
|
||||
- **Check Spelling**: Verify search terms are spelled correctly
|
||||
- **Try Alternative Keywords**: Use different or broader search terms
|
||||
- **Clear Active Filters**: Remove any filters that might be limiting results
|
||||
- **Refresh Application**: Reload the page to reset search functionality
|
||||
- **Check Indexing Status**: Some documents may take time to be indexed for search
|
||||
#### Search Not Working
|
||||
1. Clear browser cache and cookies
|
||||
2. Try different search terms or keywords
|
||||
3. Check if you're searching in the correct access level tab
|
||||
4. Verify documents exist in the location you're searching
|
||||
|
||||
#### Document Viewer Issues
|
||||
- **Browser Compatibility**: Ensure you're using a supported modern browser
|
||||
- **Enable JavaScript**: Verify JavaScript is enabled in browser settings
|
||||
- **Clear Browser Cache**: Clear cache and cookies to resolve display issues
|
||||
- **Plugin Requirements**: Install any required browser plugins for specific file types
|
||||
- **Download Alternative**: Use download option if viewer is not functioning
|
||||
#### Access Denied Errors
|
||||
1. Verify you have appropriate permissions for the document/folder
|
||||
2. Check if the document is in the correct access level category
|
||||
3. Request access from document owner or administrator
|
||||
4. Contact administrator if permissions seem incorrect
|
||||
|
||||
### Getting Help and Support
|
||||
- **Contact IT Support**: Reach out to technical support for system issues
|
||||
- **Check System Status**: Verify overall system availability and maintenance schedules
|
||||
- **User Training**: Request additional training sessions for advanced features
|
||||
- **Documentation**: Refer to technical guide for detailed system information
|
||||
- **Community Resources**: Access user forums and knowledge bases if available
|
||||
### Performance Issues
|
||||
|
||||
### Best Practices for Optimal System Use
|
||||
1. **Systematic Organization**: Create and maintain clear, logical folder structures
|
||||
2. **Descriptive Naming**: Use meaningful, descriptive document titles and filenames
|
||||
3. **Regular Maintenance**: Periodically review and clean up outdated documents
|
||||
4. **Backup Strategy**: Keep local copies of critical documents as backup
|
||||
5. **Consistent Conventions**: Follow organizational naming and filing conventions
|
||||
6. **Metadata Accuracy**: Keep document metadata current and accurate
|
||||
7. **Access Control Respect**: Follow organizational policies for document sharing and access
|
||||
8. **Version Management**: Use version control features to track document changes
|
||||
9. **Security Awareness**: Be mindful of document sensitivity and appropriate access levels
|
||||
10. **Regular Updates**: Stay informed about system updates and new features
|
||||
#### Slow Loading
|
||||
1. Check internet connection speed
|
||||
2. Clear browser cache and temporary files
|
||||
3. Close unnecessary browser tabs
|
||||
4. Contact administrator about server performance
|
||||
|
||||
#### Browser Compatibility
|
||||
- Use modern browsers (Chrome 70+, Firefox 65+, Safari 12+, Edge 79+)
|
||||
- Enable JavaScript in browser settings
|
||||
- Update browser to latest version
|
||||
- Disable conflicting browser extensions
|
||||
|
||||
### Getting Help
|
||||
|
||||
#### Contact Information
|
||||
- **System Administrator**: Contact your IT department
|
||||
- **User Support**: Check internal documentation or help desk
|
||||
- **Technical Issues**: Report to system administrator with:
|
||||
- Error messages (exact text)
|
||||
- Steps to reproduce the issue
|
||||
- Browser and version information
|
||||
- Screenshots if applicable
|
||||
|
||||
#### Best Practices
|
||||
- **Regular Backups**: Keep local copies of important documents
|
||||
- **Organize Properly**: Use consistent naming and folder structures
|
||||
- **Tag Documents**: Use relevant tags for easy searching
|
||||
- **Stay Updated**: Keep track of system updates and new features
|
||||
- **Follow Security**: Use strong passwords and follow access control policies
|
||||
|
||||
This user guide provides comprehensive coverage of the EDMS interface and functionality, designed to help users effectively navigate and utilize the document management system. The system is designed for ease of use while providing powerful document management capabilities.
|
||||
|
||||
---
|
||||
|
||||
|
@ -4,14 +4,7 @@ export default [
|
||||
"description": "Document Management System",
|
||||
"child": [
|
||||
{
|
||||
"title": "ERD",
|
||||
"path": "/dms/erd",
|
||||
"icon": "material-symbols:database-sharp",
|
||||
"child": [],
|
||||
"meta": {}
|
||||
},
|
||||
{
|
||||
"title": "Document Management",
|
||||
"title": "Document Explorer",
|
||||
"path": "/dms",
|
||||
"icon": "ic:outline-folder",
|
||||
"child": [],
|
||||
|
@ -8,14 +8,11 @@ import RsCollapseItem from "../../../components/RsCollapseItem.vue";
|
||||
import RsDropdown from "../../../components/RsDropdown.vue";
|
||||
import RsDropdownItem from "../../../components/RsDropdownItem.vue";
|
||||
import RsFieldset from "../../../components/RsFieldset.vue";
|
||||
import RsInput from "../../../components/RsInput.vue";
|
||||
import RsModal from "../../../components/RsModal.vue";
|
||||
import RsProgressBar from "../../../components/RsProgressBar.vue";
|
||||
import RsSelect from "../../../components/RsSelect.vue";
|
||||
import RsTab from "../../../components/RsTab.vue";
|
||||
import RsTabItem from "../../../components/RsTabItem.vue";
|
||||
import RsTable from "../../../components/RsTable.vue";
|
||||
import RsTextarea from "../../../components/RsTextarea.vue";
|
||||
import RsWizard from "../../../components/RsWizard.vue";
|
||||
|
||||
export {
|
||||
@ -29,13 +26,10 @@ export {
|
||||
RsDropdown,
|
||||
RsDropdownItem,
|
||||
RsFieldset,
|
||||
RsInput,
|
||||
RsModal,
|
||||
RsProgressBar,
|
||||
RsSelect,
|
||||
RsTab,
|
||||
RsTabItem,
|
||||
RsTable,
|
||||
RsTextarea,
|
||||
RsWizard,
|
||||
};
|
||||
|
@ -1,277 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const examples = ref({
|
||||
input: 'Sample text',
|
||||
select: 'option2',
|
||||
textarea: 'Sample textarea content',
|
||||
error: '',
|
||||
disabled: false
|
||||
});
|
||||
|
||||
const selectOptions = [
|
||||
{ value: 'option1', label: 'Option 1' },
|
||||
{ value: 'option2', label: 'Option 2' },
|
||||
{ value: 'option3', label: 'Option 3' }
|
||||
];
|
||||
|
||||
const buttonVariants = ['primary', 'secondary', 'info', 'success', 'warning', 'danger'];
|
||||
const buttonSizes = ['sm', 'md', 'lg'];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="design-system-page p-6">
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<rs-card>
|
||||
<template #header>
|
||||
<h1 class="text-2xl font-bold">🎨 DMS Design System</h1>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div class="space-y-8">
|
||||
|
||||
<!-- Design Principles -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Design Principles</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<rs-card>
|
||||
<template #body>
|
||||
<h3 class="font-medium mb-2">🎯 Consistency</h3>
|
||||
<p class="text-sm text-gray-600">All components follow the same design patterns and naming conventions</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
<rs-card>
|
||||
<template #body>
|
||||
<h3 class="font-medium mb-2">🔧 Modularity</h3>
|
||||
<p class="text-sm text-gray-600">Components are reusable and can be composed together</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
<rs-card>
|
||||
<template #body>
|
||||
<h3 class="font-medium mb-2">🌙 Dark Mode</h3>
|
||||
<p class="text-sm text-gray-600">All components support both light and dark themes</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Buttons -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Buttons</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="font-medium mb-2">Variants</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<rs-button v-for="variant in buttonVariants" :key="variant" :variant="variant">
|
||||
{{ variant.charAt(0).toUpperCase() + variant.slice(1) }}
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-medium mb-2">Sizes</h3>
|
||||
<div class="flex flex-wrap gap-2 items-center">
|
||||
<rs-button v-for="size in buttonSizes" :key="size" :size="size" variant="primary">
|
||||
{{ size.toUpperCase() }}
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-medium mb-2">States</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<rs-button variant="primary">Normal</rs-button>
|
||||
<rs-button variant="primary" :disabled="true">Disabled</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Form Components -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Form Components</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
|
||||
<!-- Input -->
|
||||
<div>
|
||||
<h3 class="font-medium mb-3">Input Field</h3>
|
||||
<rs-input
|
||||
v-model="examples.input"
|
||||
label="Input Label"
|
||||
placeholder="Enter text here..."
|
||||
:required="true"
|
||||
/>
|
||||
<rs-input
|
||||
v-model="examples.input"
|
||||
label="With Error"
|
||||
placeholder="Enter text here..."
|
||||
error="This field is required"
|
||||
/>
|
||||
<rs-input
|
||||
v-model="examples.input"
|
||||
label="Disabled"
|
||||
placeholder="Enter text here..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Select -->
|
||||
<div>
|
||||
<h3 class="font-medium mb-3">Select Dropdown</h3>
|
||||
<rs-select
|
||||
v-model="examples.select"
|
||||
:options="selectOptions"
|
||||
label="Select Option"
|
||||
placeholder="Choose an option"
|
||||
:required="true"
|
||||
/>
|
||||
<rs-select
|
||||
v-model="examples.select"
|
||||
:options="selectOptions"
|
||||
label="Multiple Select"
|
||||
:multiple="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Textarea -->
|
||||
<div class="md:col-span-2">
|
||||
<h3 class="font-medium mb-3">Textarea</h3>
|
||||
<rs-textarea
|
||||
v-model="examples.textarea"
|
||||
label="Message"
|
||||
placeholder="Enter your message..."
|
||||
:rows="4"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Cards -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Cards</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<rs-card>
|
||||
<template #header>
|
||||
Card with Header
|
||||
</template>
|
||||
<template #body>
|
||||
This is a card with a header section.
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<rs-card>
|
||||
<template #body>
|
||||
This is a basic card with only body content.
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<rs-card>
|
||||
<template #body>
|
||||
This card has both body and footer.
|
||||
</template>
|
||||
<template #footer>
|
||||
<rs-button size="sm" variant="primary">Action</rs-button>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Modals -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Modals</h2>
|
||||
<p class="text-gray-600 mb-4">Modals should use RsModal component with standardized header, body, and footer structure.</p>
|
||||
<div class="bg-gray-100 dark:bg-gray-800 p-4 rounded-lg">
|
||||
<pre class="text-sm"><code><rs-modal :visible="showModal" @close="closeModal">
|
||||
<template #header>
|
||||
Modal Title
|
||||
</template>
|
||||
<template #body>
|
||||
Modal content here
|
||||
</template>
|
||||
<template #footer>
|
||||
<rs-button variant="secondary">Cancel</rs-button>
|
||||
<rs-button variant="primary">Confirm</rs-button>
|
||||
</template>
|
||||
</rs-modal></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Color System -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Color System</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-primary rounded mb-2"></div>
|
||||
<span class="text-sm">Primary</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-secondary rounded mb-2"></div>
|
||||
<span class="text-sm">Secondary</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-info rounded mb-2"></div>
|
||||
<span class="text-sm">Info</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-success rounded mb-2"></div>
|
||||
<span class="text-sm">Success</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-warning rounded mb-2"></div>
|
||||
<span class="text-sm">Warning</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-full h-12 bg-danger rounded mb-2"></div>
|
||||
<span class="text-sm">Danger</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Usage Guidelines -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold mb-4">Usage Guidelines</h2>
|
||||
<div class="space-y-4">
|
||||
<rs-card>
|
||||
<template #body>
|
||||
<h3 class="font-medium mb-2">✅ Do</h3>
|
||||
<ul class="list-disc pl-6 space-y-1 text-sm">
|
||||
<li>Use RsButton, RsInput, RsSelect, RsTextarea for all form elements</li>
|
||||
<li>Use RsModal for all dialog components</li>
|
||||
<li>Follow consistent spacing and sizing patterns</li>
|
||||
<li>Use semantic variant names (primary, secondary, danger, etc.)</li>
|
||||
</ul>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<rs-card>
|
||||
<template #body>
|
||||
<h3 class="font-medium mb-2">❌ Don't</h3>
|
||||
<ul class="list-disc pl-6 space-y-1 text-sm">
|
||||
<li>Use manual button styling with px-4 py-2 classes</li>
|
||||
<li>Create custom input styles without using Rs components</li>
|
||||
<li>Use hardcoded colors instead of CSS variables</li>
|
||||
<li>Mix different component styling approaches</li>
|
||||
</ul>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
color: rgb(var(--text-color));
|
||||
}
|
||||
</style>
|
@ -1,8 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useDmsStore } from '~/stores/dms';
|
||||
import DMSNavigation from '~/components/dms/navigation/DMSNavigation.vue';
|
||||
import DMSExplorer from '~/components/dms/explorer/DMSExplorer.vue';
|
||||
import { ref, onMounted, nextTick } from 'vue';
|
||||
|
||||
// Define page metadata
|
||||
definePageMeta({
|
||||
@ -17,122 +14,90 @@ definePageMeta({
|
||||
],
|
||||
});
|
||||
|
||||
// Set up store
|
||||
const dmsStore = useDmsStore();
|
||||
// Import DMS components dynamically to handle potential import errors
|
||||
let DMSExplorer = null;
|
||||
let useDmsStore = null;
|
||||
|
||||
// Local state
|
||||
const showFileViewer = ref(false);
|
||||
const currentDocument = ref(null);
|
||||
const searchQuery = ref('');
|
||||
const isSearching = ref(false);
|
||||
const currentPath = ref('JKR Cawangan Kota Bharu, Kelantan');
|
||||
const viewMode = ref('explorer'); // explorer, cabinets, list
|
||||
const selectedItem = ref(null);
|
||||
// Basic state
|
||||
const activeTab = ref('all');
|
||||
const isLoading = ref(true);
|
||||
const hasError = ref(false);
|
||||
const errorMessage = ref('');
|
||||
const componentsLoaded = ref(false);
|
||||
|
||||
// File selection state
|
||||
const selectedFiles = ref([]);
|
||||
const isSelecting = ref(false);
|
||||
|
||||
// Toggle file selection
|
||||
const toggleFileSelection = (file) => {
|
||||
const index = selectedFiles.value.findIndex(f => f.id === file.id);
|
||||
if (index === -1) {
|
||||
selectedFiles.value.push(file);
|
||||
} else {
|
||||
selectedFiles.value.splice(index, 1);
|
||||
// Tab definitions with icons
|
||||
const tabs = [
|
||||
{
|
||||
id: 'all',
|
||||
label: 'All Documents',
|
||||
icon: 'folder',
|
||||
color: 'blue'
|
||||
},
|
||||
{
|
||||
id: 'public',
|
||||
label: 'Public',
|
||||
icon: 'unlock',
|
||||
color: 'green'
|
||||
},
|
||||
{
|
||||
id: 'private',
|
||||
label: 'Private',
|
||||
icon: 'lock',
|
||||
color: 'red'
|
||||
},
|
||||
{
|
||||
id: 'personal',
|
||||
label: 'Personal',
|
||||
icon: 'user',
|
||||
color: 'purple'
|
||||
}
|
||||
};
|
||||
|
||||
// Clear selection
|
||||
const clearSelection = () => {
|
||||
selectedFiles.value = [];
|
||||
isSelecting.value = false;
|
||||
};
|
||||
|
||||
// Select all files
|
||||
const selectAllFiles = () => {
|
||||
selectedFiles.value = [...dmsStore.currentItems];
|
||||
isSelecting.value = true;
|
||||
};
|
||||
|
||||
// Check if a file is selected
|
||||
const isFileSelected = (file) => {
|
||||
return selectedFiles.value.some(f => f.id === file.id);
|
||||
};
|
||||
|
||||
// Toggle view mode
|
||||
const changeViewMode = (mode) => {
|
||||
viewMode.value = mode;
|
||||
};
|
||||
|
||||
// View a file
|
||||
const viewFile = (file) => {
|
||||
if (isSelecting.value) {
|
||||
toggleFileSelection(file);
|
||||
return;
|
||||
}
|
||||
|
||||
currentDocument.value = file;
|
||||
showFileViewer.value = true;
|
||||
};
|
||||
|
||||
// Navigate to a location
|
||||
const navigateTo = (path) => {
|
||||
currentPath.value = path;
|
||||
// In a real app, we would fetch the contents of this location
|
||||
clearSelection();
|
||||
};
|
||||
|
||||
// Search functionality
|
||||
const handleSearch = async () => {
|
||||
if (!searchQuery.value.trim()) return;
|
||||
|
||||
isSearching.value = true;
|
||||
await dmsStore.searchDocuments(searchQuery.value);
|
||||
isSearching.value = false;
|
||||
};
|
||||
|
||||
// Clear search
|
||||
const clearSearch = () => {
|
||||
searchQuery.value = '';
|
||||
dmsStore.clearSearch();
|
||||
};
|
||||
|
||||
// Format file size
|
||||
const formatFileSize = (size) => {
|
||||
if (!size) return '0 B';
|
||||
|
||||
if (typeof size === 'string') {
|
||||
// If already formatted (like "4MB"), return as is
|
||||
if (size.endsWith('B')) return size;
|
||||
|
||||
// Try to parse the size if it's a number in string form
|
||||
const parsed = parseFloat(size);
|
||||
if (isNaN(parsed)) return size;
|
||||
size = parsed;
|
||||
}
|
||||
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let i = 0;
|
||||
while (size >= 1024 && i < units.length - 1) {
|
||||
size /= 1024;
|
||||
i++;
|
||||
}
|
||||
return `${size.toFixed(2)} ${units[i]}`;
|
||||
};
|
||||
|
||||
// Document category tabs
|
||||
const documentTabs = [
|
||||
{ id: 'all', label: 'All Documents', icon: 'folder' },
|
||||
{ id: 'public', label: 'Public', icon: 'unlock' },
|
||||
{ id: 'private', label: 'Private', icon: 'lock' },
|
||||
{ id: 'personal', label: 'Personal', icon: 'user' }
|
||||
];
|
||||
|
||||
// Handle events from explorer
|
||||
// Change active tab
|
||||
const changeTab = (tabId) => {
|
||||
activeTab.value = tabId;
|
||||
};
|
||||
|
||||
// Get SVG icon function
|
||||
const getSvgIcon = (iconName, size = 20) => {
|
||||
const icons = {
|
||||
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>`,
|
||||
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>`,
|
||||
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
|
||||
user: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`
|
||||
};
|
||||
return icons[iconName] || icons.folder;
|
||||
};
|
||||
|
||||
// Load components dynamically
|
||||
const loadComponents = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
hasError.value = false;
|
||||
|
||||
// Import components
|
||||
const dmsStoreModule = await import('~/stores/dms');
|
||||
const dmsExplorerModule = await import('~/components/dms/explorer/DMSExplorer.vue');
|
||||
|
||||
useDmsStore = dmsStoreModule.useDmsStore;
|
||||
DMSExplorer = dmsExplorerModule.default;
|
||||
|
||||
componentsLoaded.value = true;
|
||||
|
||||
await nextTick();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load DMS components:', error);
|
||||
hasError.value = true;
|
||||
errorMessage.value = `Failed to load DMS components: ${error.message}`;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Event handlers (placeholder functions for when components load)
|
||||
const handleItemSelected = (item) => {
|
||||
selectedItem.value = item;
|
||||
console.log('Item selected:', item);
|
||||
};
|
||||
|
||||
const handleViewModeChanged = (mode) => {
|
||||
@ -140,23 +105,12 @@ const handleViewModeChanged = (mode) => {
|
||||
};
|
||||
|
||||
const handlePathChanged = (path) => {
|
||||
currentPath.value = path;
|
||||
};
|
||||
|
||||
// Get SVG icon
|
||||
const getSvgIcon = (iconType, size = 16) => {
|
||||
const icons = {
|
||||
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>`,
|
||||
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>`,
|
||||
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
|
||||
user: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`
|
||||
};
|
||||
return icons[iconType] || icons.folder;
|
||||
console.log('Path changed to:', path);
|
||||
};
|
||||
|
||||
// Lifecycle hooks
|
||||
onMounted(() => {
|
||||
// Any initialization logic
|
||||
loadComponents();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -167,34 +121,86 @@ onMounted(() => {
|
||||
<rs-card class="h-full">
|
||||
<template #body>
|
||||
<div class="dms-layout h-full flex flex-col">
|
||||
<!-- Document Category Tabs -->
|
||||
<div class="tabs-header border-b border-gray-200 dark:border-gray-700 p-4 bg-white dark:bg-gray-800">
|
||||
<div class="flex items-center space-x-1">
|
||||
<button
|
||||
v-for="tab in documentTabs"
|
||||
:key="tab.id"
|
||||
@click="activeTab = tab.id"
|
||||
class="flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
:class="activeTab === tab.id
|
||||
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-300'
|
||||
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700'"
|
||||
>
|
||||
<span v-html="getSvgIcon(tab.icon, 16)"></span>
|
||||
<span>{{ tab.label }}</span>
|
||||
</button>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="isLoading" class="flex items-center justify-center h-full">
|
||||
<div class="text-center">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
||||
<p class="text-gray-600 dark:text-gray-400">Loading Document Management System...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Explorer Component -->
|
||||
<div class="explorer-container flex-1 overflow-hidden">
|
||||
<DMSExplorer
|
||||
:initial-path="'/'"
|
||||
:view-mode="'list'"
|
||||
:active-document-tab="activeTab"
|
||||
@item-selected="handleItemSelected"
|
||||
@view-mode-changed="handleViewModeChanged"
|
||||
@path-changed="handlePathChanged"
|
||||
/>
|
||||
<!-- Error State -->
|
||||
<div v-else-if="hasError" class="flex items-center justify-center h-full">
|
||||
<div class="text-center p-6">
|
||||
<div class="text-red-500 text-5xl mb-4">⚠️</div>
|
||||
<h2 class="text-xl font-semibold text-red-600 mb-2">Error Loading DMS</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-4">{{ errorMessage }}</p>
|
||||
<rs-button @click="loadComponents" variant="primary">
|
||||
Retry
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div v-else class="dms-content h-full flex flex-col">
|
||||
<!-- Enhanced Access Level Tabs -->
|
||||
<div class="access-tabs bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex items-center px-6 py-4">
|
||||
<div class="flex space-x-1">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@click="changeTab(tab.id)"
|
||||
class="access-tab flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200"
|
||||
:class="[
|
||||
activeTab === tab.id
|
||||
? `bg-${tab.color}-100 text-${tab.color}-700 border border-${tab.color}-200 shadow-sm dark:bg-${tab.color}-900/20 dark:text-${tab.color}-300 dark:border-${tab.color}-800`
|
||||
: 'text-gray-600 hover:text-gray-900 hover:bg-white dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 border border-transparent hover:border-gray-200 dark:hover:border-gray-600'
|
||||
]"
|
||||
>
|
||||
<span
|
||||
v-html="getSvgIcon(tab.icon)"
|
||||
:class="[
|
||||
activeTab === tab.id
|
||||
? `text-${tab.color}-600 dark:text-${tab.color}-400`
|
||||
: 'text-gray-500 dark:text-gray-500'
|
||||
]"
|
||||
></span>
|
||||
<span>{{ tab.label }}</span>
|
||||
<span
|
||||
v-if="activeTab === tab.id"
|
||||
:class="`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-${tab.color}-100 text-${tab.color}-800 dark:bg-${tab.color}-900/30 dark:text-${tab.color}-300`"
|
||||
>
|
||||
Active
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="content-area flex-1">
|
||||
<!-- DMS Explorer Component -->
|
||||
<component
|
||||
v-if="componentsLoaded && DMSExplorer"
|
||||
:is="DMSExplorer"
|
||||
:initial-path="'/'"
|
||||
:view-mode="'list'"
|
||||
:active-document-tab="activeTab"
|
||||
@item-selected="handleItemSelected"
|
||||
@view-mode-changed="handleViewModeChanged"
|
||||
@path-changed="handlePathChanged"
|
||||
/>
|
||||
|
||||
<!-- Fallback Content -->
|
||||
<div v-else class="text-center py-12">
|
||||
<h2 class="text-2xl font-semibold mb-4">{{ tabs.find(t => t.id === activeTab)?.label }}</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400">
|
||||
Document explorer is loading...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -211,16 +217,81 @@ onMounted(() => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.explorer-container {
|
||||
.content-area {
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Ensure smooth transitions */
|
||||
.tabs-header button {
|
||||
transition: all 0.2s ease;
|
||||
.dms-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabs-header button:hover {
|
||||
.access-tabs {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.access-tab {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.access-tab:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.access-tab.active {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Dynamic color classes */
|
||||
.bg-blue-100 { background-color: rgb(219 234 254); }
|
||||
.text-blue-700 { color: rgb(29 78 216); }
|
||||
.border-blue-200 { border-color: rgb(191 219 254); }
|
||||
.text-blue-600 { color: rgb(37 99 235); }
|
||||
.bg-blue-100 { background-color: rgb(219 234 254); }
|
||||
.text-blue-800 { color: rgb(30 64 175); }
|
||||
|
||||
.bg-green-100 { background-color: rgb(220 252 231); }
|
||||
.text-green-700 { color: rgb(21 128 61); }
|
||||
.border-green-200 { border-color: rgb(187 247 208); }
|
||||
.text-green-600 { color: rgb(22 163 74); }
|
||||
.text-green-800 { color: rgb(22 101 52); }
|
||||
|
||||
.bg-red-100 { background-color: rgb(254 226 226); }
|
||||
.text-red-700 { color: rgb(185 28 28); }
|
||||
.border-red-200 { border-color: rgb(254 202 202); }
|
||||
.text-red-600 { color: rgb(220 38 38); }
|
||||
.text-red-800 { color: rgb(153 27 27); }
|
||||
|
||||
.bg-purple-100 { background-color: rgb(243 232 255); }
|
||||
.text-purple-700 { color: rgb(126 34 206); }
|
||||
.border-purple-200 { border-color: rgb(233 213 255); }
|
||||
.text-purple-600 { color: rgb(147 51 234); }
|
||||
.text-purple-800 { color: rgb(107 33 168); }
|
||||
|
||||
/* Dark mode colors */
|
||||
.dark .bg-blue-900\/20 { background-color: rgba(30, 58, 138, 0.2); }
|
||||
.dark .text-blue-300 { color: rgb(147 197 253); }
|
||||
.dark .border-blue-800 { border-color: rgb(30 64 175); }
|
||||
.dark .text-blue-400 { color: rgb(96 165 250); }
|
||||
.dark .bg-blue-900\/30 { background-color: rgba(30, 58, 138, 0.3); }
|
||||
|
||||
.dark .bg-green-900\/20 { background-color: rgba(20, 83, 45, 0.2); }
|
||||
.dark .text-green-300 { color: rgb(134 239 172); }
|
||||
.dark .border-green-800 { border-color: rgb(22 101 52); }
|
||||
.dark .text-green-400 { color: rgb(74 222 128); }
|
||||
.dark .bg-green-900\/30 { background-color: rgba(20, 83, 45, 0.3); }
|
||||
|
||||
.dark .bg-red-900\/20 { background-color: rgba(127, 29, 29, 0.2); }
|
||||
.dark .text-red-300 { color: rgb(252 165 165); }
|
||||
.dark .border-red-800 { border-color: rgb(153 27 27); }
|
||||
.dark .text-red-400 { color: rgb(248 113 113); }
|
||||
.dark .bg-red-900\/30 { background-color: rgba(127, 29, 29, 0.3); }
|
||||
|
||||
.dark .bg-purple-900\/20 { background-color: rgba(88, 28, 135, 0.2); }
|
||||
.dark .text-purple-300 { color: rgb(196 181 253); }
|
||||
.dark .border-purple-800 { border-color: rgb(107 33 168); }
|
||||
.dark .text-purple-400 { color: rgb(168 85 247); }
|
||||
.dark .bg-purple-900\/30 { background-color: rgba(88, 28, 135, 0.3); }
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue';
|
||||
|
||||
// Define page metadata
|
||||
definePageMeta({
|
||||
@ -18,11 +18,18 @@ definePageMeta({
|
||||
],
|
||||
});
|
||||
|
||||
// Basic loading and error states
|
||||
const isLoading = ref(true);
|
||||
const isSaving = ref(false);
|
||||
const saveError = ref('');
|
||||
const saveSuccess = ref('');
|
||||
|
||||
// Settings categories
|
||||
const settingsCategories = [
|
||||
{ id: 'access', name: 'User & Access Management', icon: '🔐' },
|
||||
{ id: 'documents', name: 'Document & Folder Settings', icon: '📁' },
|
||||
{ id: 'metadata', name: 'Metadata & Tagging', icon: '📝' },
|
||||
{ id: 'workflow', name: 'Workflow & Automation', icon: '🔄' },
|
||||
{ id: 'upload', name: 'Upload & Storage Settings', icon: '📤' },
|
||||
{ id: 'system', name: 'System Settings', icon: '📅' }
|
||||
];
|
||||
@ -30,9 +37,8 @@ const settingsCategories = [
|
||||
// Current active category
|
||||
const activeCategory = ref('access');
|
||||
|
||||
// Settings data structure
|
||||
// Local reactive settings for form manipulation with default structure
|
||||
const settings = reactive({
|
||||
// User & Access Management
|
||||
access: {
|
||||
userRoles: ['Admin', 'Editor', 'Viewer', 'Uploader'],
|
||||
rbacEnabled: true,
|
||||
@ -51,8 +57,6 @@ const settings = reactive({
|
||||
sessionTimeout: 8
|
||||
}
|
||||
},
|
||||
|
||||
// Document & Folder Settings
|
||||
documents: {
|
||||
folderHierarchy: {
|
||||
maxDepth: 5,
|
||||
@ -66,7 +70,7 @@ const settings = reactive({
|
||||
},
|
||||
retention: {
|
||||
enabled: true,
|
||||
defaultDays: 2555, // 7 years
|
||||
defaultDays: 2555,
|
||||
archiveBeforeDelete: true
|
||||
},
|
||||
versionControl: {
|
||||
@ -75,8 +79,6 @@ const settings = reactive({
|
||||
autoVersioning: true
|
||||
}
|
||||
},
|
||||
|
||||
// Metadata & Tagging
|
||||
metadata: {
|
||||
customFields: [
|
||||
{ name: 'Department', type: 'dropdown', required: true },
|
||||
@ -94,28 +96,41 @@ const settings = reactive({
|
||||
rules: ['confidential-keywords', 'department-based', 'file-type']
|
||||
}
|
||||
},
|
||||
|
||||
// Upload & Storage Settings
|
||||
workflow: {
|
||||
approvalFlows: {
|
||||
enabled: true,
|
||||
defaultFlow: 'department-head-approval',
|
||||
customFlows: ['legal-review', 'finance-approval', 'director-sign-off']
|
||||
},
|
||||
notifications: {
|
||||
emailNotifications: true,
|
||||
inAppNotifications: true,
|
||||
uploadAlerts: true,
|
||||
deadlineReminders: true
|
||||
},
|
||||
automation: {
|
||||
triggers: ['document-uploaded', 'approval-completed', 'deadline-reached'],
|
||||
actions: ['move-to-folder', 'send-notification', 'create-task']
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
fileTypes: {
|
||||
allowed: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'png'],
|
||||
blocked: ['exe', 'bat', 'cmd']
|
||||
},
|
||||
fileSizeLimit: 100, // MB
|
||||
fileSizeLimit: 100,
|
||||
quotas: {
|
||||
perUser: 5000, // MB
|
||||
perGroup: 50000, // MB
|
||||
perProject: 100000 // MB
|
||||
perUser: 5000,
|
||||
perGroup: 50000,
|
||||
perProject: 100000
|
||||
},
|
||||
storage: {
|
||||
type: 'local', // local, s3, azure, google
|
||||
type: 'local',
|
||||
path: '/var/uploads/edms',
|
||||
backupEnabled: true,
|
||||
compressionEnabled: false
|
||||
}
|
||||
},
|
||||
|
||||
// System Settings
|
||||
system: {
|
||||
timezone: 'Asia/Kuala_Lumpur',
|
||||
backupSchedule: 'daily',
|
||||
@ -127,67 +142,137 @@ const settings = reactive({
|
||||
}
|
||||
});
|
||||
|
||||
// Computed properties
|
||||
const currentSettings = computed(() => {
|
||||
return settings[activeCategory.value];
|
||||
});
|
||||
|
||||
// Computed properties for array-to-string conversions
|
||||
const predefinedTagsString = computed({
|
||||
get: () => settings.metadata.tagging.predefinedTags.join(', '),
|
||||
get: () => settings.metadata?.tagging?.predefinedTags?.join(', ') || '',
|
||||
set: (value) => {
|
||||
settings.metadata.tagging.predefinedTags = value.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
|
||||
}
|
||||
});
|
||||
|
||||
const allowedFileTypesString = computed({
|
||||
get: () => settings.upload.fileTypes.allowed.join(', '),
|
||||
get: () => settings.upload?.fileTypes?.allowed?.join(', ') || '',
|
||||
set: (value) => {
|
||||
settings.upload.fileTypes.allowed = value.split(',').map(type => type.trim()).filter(type => type.length > 0);
|
||||
}
|
||||
});
|
||||
|
||||
const blockedFileTypesString = computed({
|
||||
get: () => settings.upload.fileTypes.blocked.join(', '),
|
||||
get: () => settings.upload?.fileTypes?.blocked?.join(', ') || '',
|
||||
set: (value) => {
|
||||
settings.upload.fileTypes.blocked = value.split(',').map(type => type.trim()).filter(type => type.length > 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Methods
|
||||
// Load settings from API
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
saveError.value = '';
|
||||
|
||||
const response = await $fetch('/api/dms/settings', {
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
// Merge loaded settings with defaults
|
||||
Object.assign(settings, response.data);
|
||||
console.log('Settings loaded successfully:', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading settings:', error);
|
||||
saveError.value = 'Failed to load settings. Using defaults.';
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Save settings to API
|
||||
const saveSettings = async () => {
|
||||
try {
|
||||
// In a real app, this would make an API call
|
||||
console.log('Saving settings:', settings);
|
||||
isSaving.value = true;
|
||||
saveError.value = '';
|
||||
saveSuccess.value = '';
|
||||
|
||||
// Show success message
|
||||
alert('Settings saved successfully!');
|
||||
const response = await $fetch('/api/dms/settings', {
|
||||
method: 'POST',
|
||||
body: settings
|
||||
});
|
||||
|
||||
if (response && response.statusCode === 200) {
|
||||
saveSuccess.value = 'Settings saved successfully!';
|
||||
setTimeout(() => {
|
||||
saveSuccess.value = '';
|
||||
}, 3000);
|
||||
} else {
|
||||
saveError.value = 'Failed to save settings. Please try again.';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving settings:', error);
|
||||
alert('Error saving settings. Please try again.');
|
||||
saveError.value = 'Error saving settings. Please try again.';
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetToDefaults = () => {
|
||||
// Reset to defaults
|
||||
const resetToDefaultsConfirm = async () => {
|
||||
if (confirm('Are you sure you want to reset all settings to defaults? This action cannot be undone.')) {
|
||||
// Reset logic would go here
|
||||
console.log('Resetting to defaults');
|
||||
try {
|
||||
isSaving.value = true;
|
||||
saveError.value = '';
|
||||
saveSuccess.value = '';
|
||||
|
||||
// Reset to default values
|
||||
settings.access = {
|
||||
userRoles: ['Admin', 'Editor', 'Viewer', 'Uploader'],
|
||||
rbacEnabled: true,
|
||||
userGroups: ['HR Department', 'Finance', 'IT', 'Legal'],
|
||||
permissions: {
|
||||
view: true,
|
||||
edit: true,
|
||||
delete: false,
|
||||
download: true,
|
||||
share: true
|
||||
},
|
||||
authentication: {
|
||||
ssoEnabled: false,
|
||||
mfaRequired: false,
|
||||
ldapIntegration: false,
|
||||
sessionTimeout: 8
|
||||
}
|
||||
};
|
||||
|
||||
// Save the reset settings
|
||||
await saveSettings();
|
||||
saveSuccess.value = 'Settings reset to defaults successfully!';
|
||||
} catch (error) {
|
||||
console.error('Error resetting settings:', error);
|
||||
saveError.value = 'Error resetting settings. Please try again.';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const exportSettings = () => {
|
||||
const dataStr = JSON.stringify(settings, null, 2);
|
||||
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
||||
|
||||
const exportFileDefaultName = 'edms-settings.json';
|
||||
|
||||
const linkElement = document.createElement('a');
|
||||
linkElement.setAttribute('href', dataUri);
|
||||
linkElement.setAttribute('download', exportFileDefaultName);
|
||||
linkElement.click();
|
||||
// Export settings
|
||||
const exportSettingsFile = () => {
|
||||
try {
|
||||
const dataStr = JSON.stringify(settings, null, 2);
|
||||
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
||||
|
||||
const exportFileDefaultName = 'dms-settings.json';
|
||||
|
||||
const linkElement = document.createElement('a');
|
||||
linkElement.setAttribute('href', dataUri);
|
||||
linkElement.setAttribute('download', exportFileDefaultName);
|
||||
linkElement.click();
|
||||
} catch (error) {
|
||||
console.error('Error exporting settings:', error);
|
||||
saveError.value = 'Error exporting settings.';
|
||||
}
|
||||
};
|
||||
|
||||
const importSettings = (event) => {
|
||||
// Import settings
|
||||
const importSettingsFile = (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
@ -195,9 +280,13 @@ const importSettings = (event) => {
|
||||
try {
|
||||
const importedSettings = JSON.parse(e.target.result);
|
||||
Object.assign(settings, importedSettings);
|
||||
alert('Settings imported successfully!');
|
||||
saveSuccess.value = 'Settings imported successfully!';
|
||||
setTimeout(() => {
|
||||
saveSuccess.value = '';
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
alert('Error importing settings. Please check the file format.');
|
||||
console.error('Error importing settings:', error);
|
||||
saveError.value = 'Error importing settings. Please check the file format.';
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@ -219,8 +308,10 @@ const removeCustomField = (index) => {
|
||||
|
||||
const addUserRole = () => {
|
||||
const roleName = prompt('Enter new role name:');
|
||||
if (roleName && !settings.access.userRoles.includes(roleName)) {
|
||||
settings.access.userRoles.push(roleName);
|
||||
if (roleName && roleName.trim()) {
|
||||
if (!settings.access.userRoles.includes(roleName.trim())) {
|
||||
settings.access.userRoles.push(roleName.trim());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -230,6 +321,11 @@ const removeUserRole = (role) => {
|
||||
settings.access.userRoles.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Load settings on mount
|
||||
onMounted(async () => {
|
||||
await loadSettings();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -241,46 +337,66 @@ const removeUserRole = (role) => {
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">DMS Settings</h1>
|
||||
<div class="flex space-x-2">
|
||||
<rs-button @click="exportSettings" class="!bg-gray-100 !text-gray-700 border">
|
||||
<Icon name="ic:outline-download" class="mr-2" />
|
||||
<rs-button @click="exportSettingsFile" variant="secondary-outline" size="sm" :disabled="isLoading || isSaving">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7,10 12,15 17,10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
|
||||
Export
|
||||
</rs-button>
|
||||
<label class="cursor-pointer">
|
||||
<input type="file" @change="importSettings" accept=".json" class="hidden" />
|
||||
<rs-button class="!bg-gray-100 !text-gray-700 border">
|
||||
<Icon name="ic:outline-upload" class="mr-2" />
|
||||
<input type="file" @change="importSettingsFile" accept=".json" class="hidden" :disabled="isLoading || isSaving" />
|
||||
<rs-button variant="secondary-outline" size="sm" :disabled="isLoading || isSaving">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17,8 12,3 7,8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
|
||||
Import
|
||||
</rs-button>
|
||||
</label>
|
||||
<rs-button @click="resetToDefaults" class="!bg-red-100 !text-red-700 border border-red-200">
|
||||
<Icon name="ic:outline-refresh" class="mr-2" />
|
||||
<rs-button @click="resetToDefaultsConfirm" variant="danger-outline" size="sm" :disabled="isLoading || isSaving">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><polyline points="1,4 1,10 7,10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>
|
||||
Reset
|
||||
</rs-button>
|
||||
<rs-button @click="saveSettings" class="!bg-blue-600 !text-white">
|
||||
<Icon name="ic:outline-save" class="mr-2" />
|
||||
Save Settings
|
||||
<rs-button @click="saveSettings" variant="primary" size="sm" :disabled="isLoading || isSaving">
|
||||
<svg v-if="isSaving" class="animate-spin mr-2 h-4 w-4" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17,21 17,13 7,13 7,21"></polyline><polyline points="7,3 7,8 15,8"></polyline></svg>
|
||||
{{ isSaving ? 'Saving...' : 'Save Settings' }}
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success/Error Messages -->
|
||||
<div v-if="saveSuccess" class="mt-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded">
|
||||
{{ saveSuccess }}
|
||||
</div>
|
||||
<div v-if="saveError" class="mt-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded">
|
||||
{{ saveError }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div class="settings-layout flex h-full">
|
||||
<!-- Loading State -->
|
||||
<div v-if="isLoading" class="flex items-center justify-center h-64">
|
||||
<div class="text-center">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
||||
<p class="text-gray-600 dark:text-gray-400">Loading DMS settings...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Content -->
|
||||
<div v-else class="settings-layout flex h-full">
|
||||
<!-- Settings Navigation -->
|
||||
<div class="settings-nav w-80 border-r border-gray-200 dark:border-gray-700 p-4 bg-gray-50 dark:bg-gray-800">
|
||||
<div class="space-y-2">
|
||||
<button
|
||||
<rs-button
|
||||
v-for="category in settingsCategories"
|
||||
:key="category.id"
|
||||
@click="activeCategory = category.id"
|
||||
class="w-full flex items-center space-x-3 px-4 py-3 text-left rounded-lg transition-colors"
|
||||
:class="activeCategory === category.id
|
||||
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-300'
|
||||
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'"
|
||||
:variant="activeCategory === category.id ? 'primary' : 'secondary-text'"
|
||||
size="md"
|
||||
class="w-full justify-start"
|
||||
>
|
||||
<span class="text-lg">{{ category.icon }}</span>
|
||||
<span class="text-lg mr-3">{{ category.icon }}</span>
|
||||
<span class="font-medium text-sm">{{ category.name }}</span>
|
||||
</button>
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -301,11 +417,14 @@ const removeUserRole = (role) => {
|
||||
<div class="space-y-2">
|
||||
<div v-for="role in settings.access.userRoles" :key="role" class="flex items-center justify-between bg-gray-50 dark:bg-gray-700 px-3 py-2 rounded">
|
||||
<span>{{ role }}</span>
|
||||
<button @click="removeUserRole(role)" class="text-red-500 hover:text-red-700">
|
||||
<Icon name="ic:outline-delete" size="16" />
|
||||
</button>
|
||||
<rs-button @click="removeUserRole(role)" variant="danger-text" size="sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3,6 5,6 21,6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||
</rs-button>
|
||||
</div>
|
||||
<button @click="addUserRole" class="text-blue-600 hover:text-blue-800 text-sm">+ Add Role</button>
|
||||
<rs-button @click="addUserRole" variant="primary-text" size="sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
Add Role
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@ -368,31 +487,7 @@ const removeUserRole = (role) => {
|
||||
<div v-if="activeCategory === 'documents'" class="space-y-8">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold mb-4">📁 Document & Folder Settings</h2>
|
||||
|
||||
<!-- Folder Hierarchy -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border p-6 mb-6">
|
||||
<h3 class="text-lg font-medium mb-4">Folder Hierarchies</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Maximum Folder Depth</label>
|
||||
<input type="number" v-model="settings.documents.folderHierarchy.maxDepth"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md" min="1" max="10" />
|
||||
</div>
|
||||
<div>
|
||||
<rs-select
|
||||
v-model="settings.documents.folderHierarchy.folderTemplates[0]"
|
||||
:options="[
|
||||
{ value: 'Standard', label: 'Standard' },
|
||||
{ value: 'Project-based', label: 'Project-based' },
|
||||
{ value: 'Department-based', label: 'Department-based' },
|
||||
{ value: 'Custom', label: 'Custom' }
|
||||
]"
|
||||
label="Folder Template"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Naming Conventions -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border p-6 mb-6">
|
||||
<h3 class="text-lg font-medium mb-4">Document Naming Conventions</h3>
|
||||
@ -402,11 +497,10 @@ const removeUserRole = (role) => {
|
||||
Auto-generate document names
|
||||
</label>
|
||||
<div>
|
||||
<rs-input
|
||||
v-model="settings.documents.namingConventions.pattern"
|
||||
label="Naming Pattern"
|
||||
placeholder="{department}_{title}_{date}"
|
||||
/>
|
||||
<label class="block text-sm font-medium mb-2">Naming Pattern</label>
|
||||
<input type="text" v-model="settings.documents.namingConventions.pattern"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md"
|
||||
placeholder="{department}_{title}_{date}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -459,13 +553,14 @@ const removeUserRole = (role) => {
|
||||
<input type="checkbox" v-model="field.required" class="mr-2" />
|
||||
Required
|
||||
</label>
|
||||
<button @click="removeCustomField(index)" class="text-red-500 hover:text-red-700">
|
||||
<Icon name="ic:outline-delete" size="20" />
|
||||
</button>
|
||||
<rs-button @click="removeCustomField(index)" variant="danger-text" size="sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3,6 5,6 21,6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||
</rs-button>
|
||||
</div>
|
||||
<button @click="addCustomField" class="text-blue-600 hover:text-blue-800">
|
||||
+ Add Custom Field
|
||||
</button>
|
||||
<rs-button @click="addCustomField" variant="primary-text" size="sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
Add Custom Field
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -482,12 +577,10 @@ const removeUserRole = (role) => {
|
||||
Enable Tag Suggestions
|
||||
</label>
|
||||
<div>
|
||||
<rs-textarea
|
||||
v-model="predefinedTagsString"
|
||||
label="Predefined Tags"
|
||||
placeholder="urgent, confidential, public, draft, final"
|
||||
:rows="3"
|
||||
/>
|
||||
<label class="block text-sm font-medium mb-2">Predefined Tags</label>
|
||||
<textarea v-model="predefinedTagsString"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md h-20"
|
||||
placeholder="urgent, confidential, public, draft, final"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -601,18 +694,57 @@ const removeUserRole = (role) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Placeholder for other categories -->
|
||||
<div v-if="!['access', 'documents', 'metadata', 'upload', 'system'].includes(activeCategory)" class="space-y-8">
|
||||
<div class="text-center py-12">
|
||||
<div class="text-6xl mb-4">
|
||||
{{ settingsCategories.find(c => c.id === activeCategory)?.icon }}
|
||||
<!-- Workflow Settings -->
|
||||
<div v-if="activeCategory === 'workflow'" class="space-y-8">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold mb-4">🔄 Workflow & Automation</h2>
|
||||
|
||||
<!-- Approval Flows -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border p-6 mb-6">
|
||||
<h3 class="text-lg font-medium mb-4">Approval Workflows</h3>
|
||||
<div class="space-y-4">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" v-model="settings.workflow.approvalFlows.enabled" class="mr-2" />
|
||||
Enable Approval Workflows
|
||||
</label>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Default Approval Flow</label>
|
||||
<select v-model="settings.workflow.approvalFlows.defaultFlow" class="w-full px-3 py-2 border border-gray-300 rounded-md">
|
||||
<option value="department-head-approval">Department Head Approval</option>
|
||||
<option value="legal-review">Legal Review</option>
|
||||
<option value="finance-approval">Finance Approval</option>
|
||||
<option value="director-sign-off">Director Sign-off</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border p-6">
|
||||
<h3 class="text-lg font-medium mb-4">Notification Settings</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="space-y-3">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" v-model="settings.workflow.notifications.emailNotifications" class="mr-2" />
|
||||
Email Notifications
|
||||
</label>
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" v-model="settings.workflow.notifications.inAppNotifications" class="mr-2" />
|
||||
In-App Notifications
|
||||
</label>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" v-model="settings.workflow.notifications.uploadAlerts" class="mr-2" />
|
||||
Upload Alerts
|
||||
</label>
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" v-model="settings.workflow.notifications.deadlineReminders" class="mr-2" />
|
||||
Deadline Reminders
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ settingsCategories.find(c => c.id === activeCategory)?.name }}
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400">
|
||||
Settings for this category are being developed and will be available in the next update.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -12,52 +12,94 @@ model audit {
|
||||
auditIP String? @db.VarChar(255)
|
||||
auditURL String? @db.VarChar(255)
|
||||
auditURLMethod String? @db.VarChar(255)
|
||||
auditURLPayload String? @db.VarChar(255)
|
||||
auditCreatedDate DateTime? @db.DateTime(0)
|
||||
auditURLPayload String? @db.Text
|
||||
auditCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
||||
auditAction String? @db.VarChar(255)
|
||||
auditDetails String? @db.Text
|
||||
auditUserID Int?
|
||||
auditUsername String? @db.VarChar(255)
|
||||
user user? @relation(fields: [auditUserID], references: [userID])
|
||||
|
||||
@@index([auditUserID], map: "FK_audit_user")
|
||||
}
|
||||
|
||||
model lookup {
|
||||
lookupID Int @id @default(autoincrement())
|
||||
lookupOrder Int?
|
||||
lookupTitle String? @db.VarChar(255)
|
||||
lookupRefCode String? @db.VarChar(255)
|
||||
lookupValue String? @db.VarChar(255)
|
||||
lookupType String? @db.VarChar(255)
|
||||
lookupStatus String? @db.VarChar(255)
|
||||
lookupCreatedDate DateTime? @db.DateTime(0)
|
||||
lookupModifiedDate DateTime? @db.DateTime(0)
|
||||
model organization {
|
||||
org_id Int @id @default(autoincrement())
|
||||
org_name String @unique(map: "organization_unique") @db.VarChar(255)
|
||||
org_address1 String? @db.VarChar(255)
|
||||
org_address2 String? @db.VarChar(255)
|
||||
org_postcode Int?
|
||||
org_state String? @db.VarChar(100)
|
||||
org_country String? @db.VarChar(100)
|
||||
org_active Int?
|
||||
departments department[]
|
||||
}
|
||||
|
||||
model department {
|
||||
dp_id Int @id @default(autoincrement())
|
||||
dp_name String @db.VarChar(255)
|
||||
org_id Int
|
||||
cabinets cabinets[]
|
||||
organization organization @relation(fields: [org_id], references: [org_id], onDelete: Cascade, onUpdate: NoAction, map: "department_organization_FK")
|
||||
users sys_user[]
|
||||
|
||||
@@index([org_id], map: "department_organization_FK")
|
||||
}
|
||||
|
||||
model cabinets {
|
||||
cb_id Int @id @default(autoincrement())
|
||||
cb_name String @unique(map: "cabinet_master_unique") @db.VarChar(255)
|
||||
cb_parent_id Int?
|
||||
cb_private Int? @default(0)
|
||||
cb_owner Int?
|
||||
dp_id Int?
|
||||
created_at DateTime? @db.DateTime(0)
|
||||
modified_at DateTime? @db.DateTime(0)
|
||||
department department? @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "cabinets_department_FK")
|
||||
|
||||
@@index([dp_id], map: "cabinets_department_FK")
|
||||
}
|
||||
|
||||
model sys_user {
|
||||
su_id Int @id @default(autoincrement())
|
||||
su_username String @unique(map: "sys_user_unique") @db.VarChar(100)
|
||||
su_name String @db.VarChar(255)
|
||||
su_nric Int @unique(map: "sys_user_unique_1")
|
||||
su_dob DateTime @db.Date
|
||||
su_email String? @db.VarChar(255)
|
||||
su_password String @db.VarChar(255)
|
||||
dp_id Int
|
||||
su_active Int? @default(1)
|
||||
su_lock Int? @default(0)
|
||||
su_org_id Int
|
||||
department department @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "sys_user_department_FK")
|
||||
|
||||
@@index([dp_id], map: "sys_user_department_FK")
|
||||
}
|
||||
|
||||
model role {
|
||||
roleID Int @id @default(autoincrement())
|
||||
roleName String? @db.VarChar(255)
|
||||
roleDescription String? @db.VarChar(255)
|
||||
roleStatus String? @db.VarChar(255)
|
||||
roleCreatedDate DateTime? @db.DateTime(0)
|
||||
roleModifiedDate DateTime? @db.DateTime(0)
|
||||
roleID Int @id @default(autoincrement())
|
||||
roleName String? @db.VarChar(255)
|
||||
roleDescription String? @db.VarChar(255)
|
||||
roleStatus String? @db.VarChar(255)
|
||||
roleCreatedDate DateTime? @db.DateTime(0)
|
||||
roleModifiedDate DateTime? @db.DateTime(0)
|
||||
userrole userrole[]
|
||||
permissions AccessPermission[]
|
||||
}
|
||||
|
||||
model user {
|
||||
userID Int @id @default(autoincrement())
|
||||
userSecretKey String? @db.VarChar(255)
|
||||
userUsername String? @db.VarChar(255)
|
||||
userPassword String? @db.VarChar(255)
|
||||
userFullName String? @db.VarChar(255)
|
||||
userEmail String? @db.VarChar(255)
|
||||
userPhone String? @db.VarChar(255)
|
||||
userStatus String? @db.VarChar(255)
|
||||
userCreatedDate DateTime? @db.DateTime(0)
|
||||
userModifiedDate DateTime? @db.DateTime(0)
|
||||
userID Int @id @default(autoincrement())
|
||||
userSecretKey String? @db.VarChar(255)
|
||||
userUsername String? @db.VarChar(255)
|
||||
userPassword String? @db.VarChar(255)
|
||||
userFullName String? @db.VarChar(255)
|
||||
userEmail String? @db.VarChar(255)
|
||||
userPhone String? @db.VarChar(255)
|
||||
userStatus String? @db.VarChar(255)
|
||||
userCreatedDate DateTime? @db.DateTime(0)
|
||||
userModifiedDate DateTime? @db.DateTime(0)
|
||||
audit audit[]
|
||||
userrole userrole[]
|
||||
accessRequests AccessRequest[]
|
||||
permissions AccessPermission[]
|
||||
documents Document[] @relation("DocumentCreator")
|
||||
cabinets Cabinet[] @relation("CabinetCreator")
|
||||
drawers Drawer[] @relation("DrawerCreator")
|
||||
folders Folder[] @relation("FolderCreator")
|
||||
subfolders Subfolder[] @relation("SubfolderCreator")
|
||||
}
|
||||
|
||||
model userrole {
|
||||
@ -73,198 +115,105 @@ model userrole {
|
||||
}
|
||||
|
||||
model site_settings {
|
||||
settingID Int @id @default(autoincrement())
|
||||
siteName String? @db.VarChar(255)
|
||||
siteNameFontSize Int? @default(18)
|
||||
siteDescription String? @db.Text
|
||||
siteLogo String? @db.VarChar(500)
|
||||
siteLoadingLogo String? @db.VarChar(500)
|
||||
siteFavicon String? @db.VarChar(500)
|
||||
showSiteNameInHeader Boolean? @default(true)
|
||||
primaryColor String? @db.VarChar(50)
|
||||
secondaryColor String? @db.VarChar(50)
|
||||
successColor String? @db.VarChar(50)
|
||||
infoColor String? @db.VarChar(50)
|
||||
warningColor String? @db.VarChar(50)
|
||||
dangerColor String? @db.VarChar(50)
|
||||
customCSS String? @db.Text
|
||||
themeMode String? @db.VarChar(50)
|
||||
customThemeFile String? @db.VarChar(500)
|
||||
currentFont String? @db.VarChar(255)
|
||||
fontSource String? @db.VarChar(500)
|
||||
seoTitle String? @db.VarChar(255)
|
||||
seoDescription String? @db.Text
|
||||
seoKeywords String? @db.Text
|
||||
seoAuthor String? @db.VarChar(255)
|
||||
seoOgImage String? @db.VarChar(500)
|
||||
seoTwitterCard String? @default("summary_large_image") @db.VarChar(50)
|
||||
seoCanonicalUrl String? @db.VarChar(500)
|
||||
seoRobots String? @default("index, follow") @db.VarChar(100)
|
||||
seoGoogleAnalytics String? @db.VarChar(255)
|
||||
seoGoogleTagManager String? @db.VarChar(255)
|
||||
seoFacebookPixel String? @db.VarChar(255)
|
||||
settingCreatedDate DateTime? @db.DateTime(0)
|
||||
settingModifiedDate DateTime? @db.DateTime(0)
|
||||
siteLoginLogo String? @db.VarChar(500)
|
||||
settingID Int @id @default(autoincrement())
|
||||
siteName String? @default("corradAF") @db.VarChar(255)
|
||||
siteNameFontSize Int? @default(18)
|
||||
siteDescription String? @db.Text
|
||||
siteLogo String? @db.VarChar(500)
|
||||
siteLoadingLogo String? @db.VarChar(500)
|
||||
siteFavicon String? @db.VarChar(500)
|
||||
siteLoginLogo String? @db.VarChar(500)
|
||||
showSiteNameInHeader Boolean? @default(true)
|
||||
customCSS String? @db.LongText
|
||||
themeMode String? @default("biasa") @db.VarChar(100)
|
||||
customThemeFile String? @db.VarChar(500)
|
||||
currentFont String? @db.VarChar(100)
|
||||
fontSource String? @db.VarChar(100)
|
||||
seoTitle String? @db.VarChar(255)
|
||||
seoDescription String? @db.Text
|
||||
seoKeywords String? @db.Text
|
||||
seoAuthor String? @db.VarChar(255)
|
||||
seoOgImage String? @db.VarChar(500)
|
||||
seoTwitterCard String? @default("summary_large_image") @db.VarChar(100)
|
||||
seoCanonicalUrl String? @db.VarChar(500)
|
||||
seoRobots String? @default("index, follow") @db.VarChar(100)
|
||||
seoGoogleAnalytics String? @db.VarChar(255)
|
||||
seoGoogleTagManager String? @db.VarChar(255)
|
||||
seoFacebookPixel String? @db.VarChar(255)
|
||||
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
||||
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
||||
}
|
||||
|
||||
// DMS Models
|
||||
|
||||
model Cabinet {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @db.VarChar(255)
|
||||
description String? @db.Text
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
createdBy Int
|
||||
status String @default("active") @db.VarChar(50)
|
||||
user user @relation("CabinetCreator", fields: [createdBy], references: [userID])
|
||||
drawers Drawer[]
|
||||
permissions AccessPermission[]
|
||||
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model Drawer {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @db.VarChar(255)
|
||||
description String? @db.Text
|
||||
cabinetId Int
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
createdBy Int
|
||||
status String @default("active") @db.VarChar(50)
|
||||
cabinet Cabinet @relation(fields: [cabinetId], references: [id], onDelete: Cascade)
|
||||
user user @relation("DrawerCreator", fields: [createdBy], references: [userID])
|
||||
folders Folder[]
|
||||
permissions AccessPermission[]
|
||||
|
||||
@@index([cabinetId])
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model Folder {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @db.VarChar(255)
|
||||
description String? @db.Text
|
||||
drawerId Int
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
createdBy Int
|
||||
status String @default("active") @db.VarChar(50)
|
||||
drawer Drawer @relation(fields: [drawerId], references: [id], onDelete: Cascade)
|
||||
user user @relation("FolderCreator", fields: [createdBy], references: [userID])
|
||||
subfolders Subfolder[]
|
||||
documents Document[] @relation("FolderDocuments")
|
||||
permissions AccessPermission[]
|
||||
|
||||
@@index([drawerId])
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model Subfolder {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @db.VarChar(255)
|
||||
description String? @db.Text
|
||||
folderId Int
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
createdBy Int
|
||||
status String @default("active") @db.VarChar(50)
|
||||
folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
|
||||
user user @relation("SubfolderCreator", fields: [createdBy], references: [userID])
|
||||
documents Document[] @relation("SubfolderDocuments")
|
||||
permissions AccessPermission[]
|
||||
|
||||
@@index([folderId])
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model Document {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @db.VarChar(255)
|
||||
description String? @db.Text
|
||||
fileSize Int @default(0)
|
||||
fileType String @db.VarChar(100)
|
||||
fileExtension String @db.VarChar(20)
|
||||
filePath String @db.VarChar(500)
|
||||
version Int @default(1)
|
||||
isTemplate Boolean @default(false)
|
||||
isPublic Boolean @default(false)
|
||||
folderId Int?
|
||||
subfolderId Int?
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
createdBy Int
|
||||
status String @default("active") @db.VarChar(50)
|
||||
folder Folder? @relation("FolderDocuments", fields: [folderId], references: [id], onDelete: SetNull)
|
||||
subfolder Subfolder? @relation("SubfolderDocuments", fields: [subfolderId], references: [id], onDelete: SetNull)
|
||||
user user @relation("DocumentCreator", fields: [createdBy], references: [userID])
|
||||
accessRequests AccessRequest[]
|
||||
permissions AccessPermission[]
|
||||
versions DocumentVersion[]
|
||||
|
||||
@@index([folderId])
|
||||
@@index([subfolderId])
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model DocumentVersion {
|
||||
id Int @id @default(autoincrement())
|
||||
documentId Int
|
||||
version Int
|
||||
filePath String @db.VarChar(500)
|
||||
fileSize Int @default(0)
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
createdBy Int
|
||||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([documentId])
|
||||
}
|
||||
|
||||
model AccessRequest {
|
||||
id Int @id @default(autoincrement())
|
||||
documentId Int
|
||||
userId Int
|
||||
requestedLevel String @db.VarChar(50) // view, download, print, edit, full
|
||||
justification String? @db.Text
|
||||
status String @default("pending") @db.VarChar(50) // pending, approved, rejected
|
||||
responseNote String? @db.Text
|
||||
requestedAt DateTime @default(now()) @db.DateTime(0)
|
||||
respondedAt DateTime? @db.DateTime(0)
|
||||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
user user @relation(fields: [userId], references: [userID])
|
||||
|
||||
@@index([documentId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model AccessPermission {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int?
|
||||
roleId Int?
|
||||
documentId Int?
|
||||
cabinetId Int?
|
||||
drawerId Int?
|
||||
folderId Int?
|
||||
subfolderId Int?
|
||||
permissionLevel String @db.VarChar(50) // view, download, print, edit, full
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @updatedAt @db.DateTime(0)
|
||||
expiresAt DateTime? @db.DateTime(0)
|
||||
user user? @relation(fields: [userId], references: [userID], onDelete: SetNull)
|
||||
role role? @relation(fields: [roleId], references: [roleID], onDelete: SetNull)
|
||||
document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
cabinet Cabinet? @relation(fields: [cabinetId], references: [id], onDelete: Cascade)
|
||||
drawer Drawer? @relation(fields: [drawerId], references: [id], onDelete: Cascade)
|
||||
folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
|
||||
subfolder Subfolder? @relation(fields: [subfolderId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@index([roleId])
|
||||
@@index([documentId])
|
||||
@@index([cabinetId])
|
||||
@@index([drawerId])
|
||||
@@index([folderId])
|
||||
@@index([subfolderId])
|
||||
model dms_settings {
|
||||
settingID Int @id @default(autoincrement())
|
||||
// User & Access Management
|
||||
userRoles String? @db.Text
|
||||
rbacEnabled Boolean? @default(true)
|
||||
userGroups String? @db.Text
|
||||
permissionView Boolean? @default(true)
|
||||
permissionEdit Boolean? @default(true)
|
||||
permissionDelete Boolean? @default(false)
|
||||
permissionDownload Boolean? @default(true)
|
||||
permissionShare Boolean? @default(true)
|
||||
ssoEnabled Boolean? @default(false)
|
||||
mfaRequired Boolean? @default(false)
|
||||
ldapIntegration Boolean? @default(false)
|
||||
sessionTimeout Int? @default(8)
|
||||
|
||||
// Document & Folder Settings
|
||||
folderMaxDepth Int? @default(5)
|
||||
folderDefaultStructure String? @db.Text
|
||||
folderTemplates String? @db.Text
|
||||
namingAutoGenerate Boolean? @default(true)
|
||||
namingMandatoryFields String? @db.Text
|
||||
namingPattern String? @default("{department}_{title}_{date}") @db.VarChar(255)
|
||||
retentionEnabled Boolean? @default(true)
|
||||
retentionDefaultDays Int? @default(2555)
|
||||
retentionArchiveBeforeDelete Boolean? @default(true)
|
||||
versionControlEnabled Boolean? @default(true)
|
||||
versionControlMaxVersions Int? @default(10)
|
||||
versionControlAutoVersioning Boolean? @default(true)
|
||||
|
||||
// Metadata & Tagging
|
||||
metadataCustomFields String? @db.LongText
|
||||
taggingPredefinedTags String? @db.Text
|
||||
taggingUserGeneratedTags Boolean? @default(true)
|
||||
taggingTagSuggestions Boolean? @default(true)
|
||||
classificationAutoEnabled Boolean? @default(true)
|
||||
classificationRules String? @db.Text
|
||||
|
||||
// Workflow & Automation
|
||||
workflowApprovalEnabled Boolean? @default(true)
|
||||
workflowDefaultFlow String? @default("department-head-approval") @db.VarChar(255)
|
||||
workflowCustomFlows String? @db.Text
|
||||
notificationEmail Boolean? @default(true)
|
||||
notificationInApp Boolean? @default(true)
|
||||
notificationUploadAlerts Boolean? @default(true)
|
||||
notificationDeadlineReminders Boolean? @default(true)
|
||||
automationTriggers String? @db.Text
|
||||
automationActions String? @db.Text
|
||||
|
||||
// Upload & Storage Settings
|
||||
uploadAllowedFileTypes String? @db.Text
|
||||
uploadBlockedFileTypes String? @db.Text
|
||||
uploadFileSizeLimit Int? @default(100)
|
||||
uploadQuotaPerUser Int? @default(5000)
|
||||
uploadQuotaPerGroup Int? @default(50000)
|
||||
uploadQuotaPerProject Int? @default(100000)
|
||||
storageType String? @default("local") @db.VarChar(100)
|
||||
storagePath String? @default("/var/uploads/edms") @db.VarChar(500)
|
||||
storageBackupEnabled Boolean? @default(true)
|
||||
storageCompressionEnabled Boolean? @default(false)
|
||||
|
||||
// System Settings
|
||||
systemTimezone String? @default("Asia/Kuala_Lumpur") @db.VarChar(100)
|
||||
systemBackupSchedule String? @default("daily") @db.VarChar(100)
|
||||
systemLogLevel String? @default("info") @db.VarChar(100)
|
||||
systemMaintenanceMode Boolean? @default(false)
|
||||
systemAutoUpdates Boolean? @default(false)
|
||||
systemMonitoring Boolean? @default(true)
|
||||
systemPerformanceMetrics Boolean? @default(true)
|
||||
|
||||
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
||||
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
||||
}
|
||||
|
350
server/api/dms/settings.js
Normal file
350
server/api/dms/settings.js
Normal file
@ -0,0 +1,350 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const method = getMethod(event);
|
||||
|
||||
try {
|
||||
if (method === "GET") {
|
||||
// Get DMS settings
|
||||
let settings = await prisma.dms_settings.findFirst({
|
||||
orderBy: { settingID: "desc" },
|
||||
});
|
||||
|
||||
// If no settings exist, create default ones
|
||||
if (!settings) {
|
||||
settings = await prisma.dms_settings.create({
|
||||
data: {
|
||||
settingCreatedDate: new Date(),
|
||||
settingModifiedDate: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Transform database fields to frontend structure
|
||||
const transformedSettings = {
|
||||
// User & Access Management
|
||||
access: {
|
||||
userRoles: settings.userRoles ? settings.userRoles.split(',') : ['Admin', 'Editor', 'Viewer', 'Uploader'],
|
||||
rbacEnabled: settings.rbacEnabled ?? true,
|
||||
userGroups: settings.userGroups ? settings.userGroups.split(',') : ['HR Department', 'Finance', 'IT', 'Legal'],
|
||||
permissions: {
|
||||
view: settings.permissionView ?? true,
|
||||
edit: settings.permissionEdit ?? true,
|
||||
delete: settings.permissionDelete ?? false,
|
||||
download: settings.permissionDownload ?? true,
|
||||
share: settings.permissionShare ?? true
|
||||
},
|
||||
authentication: {
|
||||
ssoEnabled: settings.ssoEnabled ?? false,
|
||||
mfaRequired: settings.mfaRequired ?? false,
|
||||
ldapIntegration: settings.ldapIntegration ?? false,
|
||||
sessionTimeout: settings.sessionTimeout ?? 8
|
||||
}
|
||||
},
|
||||
|
||||
// Document & Folder Settings
|
||||
documents: {
|
||||
folderHierarchy: {
|
||||
maxDepth: settings.folderMaxDepth ?? 5,
|
||||
defaultStructure: settings.folderDefaultStructure ? settings.folderDefaultStructure.split(',') : ['Department', 'Project', 'Category', 'Year'],
|
||||
folderTemplates: settings.folderTemplates ? settings.folderTemplates.split(',') : ['Standard', 'Project-based', 'Department-based']
|
||||
},
|
||||
namingConventions: {
|
||||
autoGenerate: settings.namingAutoGenerate ?? true,
|
||||
mandatoryFields: settings.namingMandatoryFields ? settings.namingMandatoryFields.split(',') : ['title', 'department', 'date'],
|
||||
pattern: settings.namingPattern ?? '{department}_{title}_{date}'
|
||||
},
|
||||
retention: {
|
||||
enabled: settings.retentionEnabled ?? true,
|
||||
defaultDays: settings.retentionDefaultDays ?? 2555,
|
||||
archiveBeforeDelete: settings.retentionArchiveBeforeDelete ?? true
|
||||
},
|
||||
versionControl: {
|
||||
enabled: settings.versionControlEnabled ?? true,
|
||||
maxVersions: settings.versionControlMaxVersions ?? 10,
|
||||
autoVersioning: settings.versionControlAutoVersioning ?? true
|
||||
}
|
||||
},
|
||||
|
||||
// Metadata & Tagging
|
||||
metadata: {
|
||||
customFields: settings.metadataCustomFields ? JSON.parse(settings.metadataCustomFields) : [
|
||||
{ name: 'Department', type: 'dropdown', required: true },
|
||||
{ name: 'Priority', type: 'select', required: false },
|
||||
{ name: 'Project Code', type: 'text', required: true },
|
||||
{ name: 'Review Date', type: 'date', required: false }
|
||||
],
|
||||
tagging: {
|
||||
predefinedTags: settings.taggingPredefinedTags ? settings.taggingPredefinedTags.split(',') : ['urgent', 'confidential', 'public', 'draft', 'final'],
|
||||
userGeneratedTags: settings.taggingUserGeneratedTags ?? true,
|
||||
tagSuggestions: settings.taggingTagSuggestions ?? true
|
||||
},
|
||||
classification: {
|
||||
autoClassification: settings.classificationAutoEnabled ?? true,
|
||||
rules: settings.classificationRules ? settings.classificationRules.split(',') : ['confidential-keywords', 'department-based', 'file-type']
|
||||
}
|
||||
},
|
||||
|
||||
// Workflow & Automation
|
||||
workflow: {
|
||||
approvalFlows: {
|
||||
enabled: settings.workflowApprovalEnabled ?? true,
|
||||
defaultFlow: settings.workflowDefaultFlow ?? 'department-head-approval',
|
||||
customFlows: settings.workflowCustomFlows ? settings.workflowCustomFlows.split(',') : ['legal-review', 'finance-approval', 'director-sign-off']
|
||||
},
|
||||
notifications: {
|
||||
emailNotifications: settings.notificationEmail ?? true,
|
||||
inAppNotifications: settings.notificationInApp ?? true,
|
||||
uploadAlerts: settings.notificationUploadAlerts ?? true,
|
||||
deadlineReminders: settings.notificationDeadlineReminders ?? true
|
||||
},
|
||||
automation: {
|
||||
triggers: settings.automationTriggers ? settings.automationTriggers.split(',') : ['document-uploaded', 'approval-completed', 'deadline-reached'],
|
||||
actions: settings.automationActions ? settings.automationActions.split(',') : ['move-to-folder', 'send-notification', 'create-task']
|
||||
}
|
||||
},
|
||||
|
||||
// Upload & Storage Settings
|
||||
upload: {
|
||||
fileTypes: {
|
||||
allowed: settings.uploadAllowedFileTypes ? settings.uploadAllowedFileTypes.split(',') : ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'png'],
|
||||
blocked: settings.uploadBlockedFileTypes ? settings.uploadBlockedFileTypes.split(',') : ['exe', 'bat', 'cmd']
|
||||
},
|
||||
fileSizeLimit: settings.uploadFileSizeLimit ?? 100,
|
||||
quotas: {
|
||||
perUser: settings.uploadQuotaPerUser ?? 5000,
|
||||
perGroup: settings.uploadQuotaPerGroup ?? 50000,
|
||||
perProject: settings.uploadQuotaPerProject ?? 100000
|
||||
},
|
||||
storage: {
|
||||
type: settings.storageType ?? 'local',
|
||||
path: settings.storagePath ?? '/var/uploads/edms',
|
||||
backupEnabled: settings.storageBackupEnabled ?? true,
|
||||
compressionEnabled: settings.storageCompressionEnabled ?? false
|
||||
}
|
||||
},
|
||||
|
||||
// System Settings
|
||||
system: {
|
||||
timezone: settings.systemTimezone ?? 'Asia/Kuala_Lumpur',
|
||||
backupSchedule: settings.systemBackupSchedule ?? 'daily',
|
||||
logLevel: settings.systemLogLevel ?? 'info',
|
||||
maintenanceMode: settings.systemMaintenanceMode ?? false,
|
||||
autoUpdates: settings.systemAutoUpdates ?? false,
|
||||
systemMonitoring: settings.systemMonitoring ?? true,
|
||||
performanceMetrics: settings.systemPerformanceMetrics ?? true
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Success",
|
||||
data: transformedSettings,
|
||||
};
|
||||
}
|
||||
|
||||
if (method === "POST") {
|
||||
let body;
|
||||
try {
|
||||
body = await readBody(event);
|
||||
} catch (bodyError) {
|
||||
console.error("Error reading request body:", bodyError);
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Invalid request body",
|
||||
error: bodyError.message,
|
||||
};
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!body || typeof body !== 'object') {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Request body must be a valid JSON object",
|
||||
};
|
||||
}
|
||||
|
||||
// Check if settings exist
|
||||
const existingSettings = await prisma.dms_settings.findFirst();
|
||||
|
||||
// Transform frontend structure to database fields
|
||||
const dbData = {
|
||||
settingModifiedDate: new Date()
|
||||
};
|
||||
|
||||
// User & Access Management
|
||||
if (body.access) {
|
||||
if (body.access.userRoles) dbData.userRoles = body.access.userRoles.join(',');
|
||||
if (body.access.rbacEnabled !== undefined) dbData.rbacEnabled = body.access.rbacEnabled;
|
||||
if (body.access.userGroups) dbData.userGroups = body.access.userGroups.join(',');
|
||||
if (body.access.permissions) {
|
||||
if (body.access.permissions.view !== undefined) dbData.permissionView = body.access.permissions.view;
|
||||
if (body.access.permissions.edit !== undefined) dbData.permissionEdit = body.access.permissions.edit;
|
||||
if (body.access.permissions.delete !== undefined) dbData.permissionDelete = body.access.permissions.delete;
|
||||
if (body.access.permissions.download !== undefined) dbData.permissionDownload = body.access.permissions.download;
|
||||
if (body.access.permissions.share !== undefined) dbData.permissionShare = body.access.permissions.share;
|
||||
}
|
||||
if (body.access.authentication) {
|
||||
if (body.access.authentication.ssoEnabled !== undefined) dbData.ssoEnabled = body.access.authentication.ssoEnabled;
|
||||
if (body.access.authentication.mfaRequired !== undefined) dbData.mfaRequired = body.access.authentication.mfaRequired;
|
||||
if (body.access.authentication.ldapIntegration !== undefined) dbData.ldapIntegration = body.access.authentication.ldapIntegration;
|
||||
if (body.access.authentication.sessionTimeout !== undefined) dbData.sessionTimeout = body.access.authentication.sessionTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
// Document & Folder Settings
|
||||
if (body.documents) {
|
||||
if (body.documents.folderHierarchy) {
|
||||
if (body.documents.folderHierarchy.maxDepth !== undefined) dbData.folderMaxDepth = body.documents.folderHierarchy.maxDepth;
|
||||
if (body.documents.folderHierarchy.defaultStructure) dbData.folderDefaultStructure = body.documents.folderHierarchy.defaultStructure.join(',');
|
||||
if (body.documents.folderHierarchy.folderTemplates) dbData.folderTemplates = body.documents.folderHierarchy.folderTemplates.join(',');
|
||||
}
|
||||
if (body.documents.namingConventions) {
|
||||
if (body.documents.namingConventions.autoGenerate !== undefined) dbData.namingAutoGenerate = body.documents.namingConventions.autoGenerate;
|
||||
if (body.documents.namingConventions.mandatoryFields) dbData.namingMandatoryFields = body.documents.namingConventions.mandatoryFields.join(',');
|
||||
if (body.documents.namingConventions.pattern !== undefined) dbData.namingPattern = body.documents.namingConventions.pattern;
|
||||
}
|
||||
if (body.documents.retention) {
|
||||
if (body.documents.retention.enabled !== undefined) dbData.retentionEnabled = body.documents.retention.enabled;
|
||||
if (body.documents.retention.defaultDays !== undefined) dbData.retentionDefaultDays = body.documents.retention.defaultDays;
|
||||
if (body.documents.retention.archiveBeforeDelete !== undefined) dbData.retentionArchiveBeforeDelete = body.documents.retention.archiveBeforeDelete;
|
||||
}
|
||||
if (body.documents.versionControl) {
|
||||
if (body.documents.versionControl.enabled !== undefined) dbData.versionControlEnabled = body.documents.versionControl.enabled;
|
||||
if (body.documents.versionControl.maxVersions !== undefined) dbData.versionControlMaxVersions = body.documents.versionControl.maxVersions;
|
||||
if (body.documents.versionControl.autoVersioning !== undefined) dbData.versionControlAutoVersioning = body.documents.versionControl.autoVersioning;
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata & Tagging
|
||||
if (body.metadata) {
|
||||
if (body.metadata.customFields) dbData.metadataCustomFields = JSON.stringify(body.metadata.customFields);
|
||||
if (body.metadata.tagging) {
|
||||
if (body.metadata.tagging.predefinedTags) dbData.taggingPredefinedTags = body.metadata.tagging.predefinedTags.join(',');
|
||||
if (body.metadata.tagging.userGeneratedTags !== undefined) dbData.taggingUserGeneratedTags = body.metadata.tagging.userGeneratedTags;
|
||||
if (body.metadata.tagging.tagSuggestions !== undefined) dbData.taggingTagSuggestions = body.metadata.tagging.tagSuggestions;
|
||||
}
|
||||
if (body.metadata.classification) {
|
||||
if (body.metadata.classification.autoClassification !== undefined) dbData.classificationAutoEnabled = body.metadata.classification.autoClassification;
|
||||
if (body.metadata.classification.rules) dbData.classificationRules = body.metadata.classification.rules.join(',');
|
||||
}
|
||||
}
|
||||
|
||||
// Workflow & Automation
|
||||
if (body.workflow) {
|
||||
if (body.workflow.approvalFlows) {
|
||||
if (body.workflow.approvalFlows.enabled !== undefined) dbData.workflowApprovalEnabled = body.workflow.approvalFlows.enabled;
|
||||
if (body.workflow.approvalFlows.defaultFlow !== undefined) dbData.workflowDefaultFlow = body.workflow.approvalFlows.defaultFlow;
|
||||
if (body.workflow.approvalFlows.customFlows) dbData.workflowCustomFlows = body.workflow.approvalFlows.customFlows.join(',');
|
||||
}
|
||||
if (body.workflow.notifications) {
|
||||
if (body.workflow.notifications.emailNotifications !== undefined) dbData.notificationEmail = body.workflow.notifications.emailNotifications;
|
||||
if (body.workflow.notifications.inAppNotifications !== undefined) dbData.notificationInApp = body.workflow.notifications.inAppNotifications;
|
||||
if (body.workflow.notifications.uploadAlerts !== undefined) dbData.notificationUploadAlerts = body.workflow.notifications.uploadAlerts;
|
||||
if (body.workflow.notifications.deadlineReminders !== undefined) dbData.notificationDeadlineReminders = body.workflow.notifications.deadlineReminders;
|
||||
}
|
||||
if (body.workflow.automation) {
|
||||
if (body.workflow.automation.triggers) dbData.automationTriggers = body.workflow.automation.triggers.join(',');
|
||||
if (body.workflow.automation.actions) dbData.automationActions = body.workflow.automation.actions.join(',');
|
||||
}
|
||||
}
|
||||
|
||||
// Upload & Storage Settings
|
||||
if (body.upload) {
|
||||
if (body.upload.fileTypes) {
|
||||
if (body.upload.fileTypes.allowed) dbData.uploadAllowedFileTypes = body.upload.fileTypes.allowed.join(',');
|
||||
if (body.upload.fileTypes.blocked) dbData.uploadBlockedFileTypes = body.upload.fileTypes.blocked.join(',');
|
||||
}
|
||||
if (body.upload.fileSizeLimit !== undefined) dbData.uploadFileSizeLimit = body.upload.fileSizeLimit;
|
||||
if (body.upload.quotas) {
|
||||
if (body.upload.quotas.perUser !== undefined) dbData.uploadQuotaPerUser = body.upload.quotas.perUser;
|
||||
if (body.upload.quotas.perGroup !== undefined) dbData.uploadQuotaPerGroup = body.upload.quotas.perGroup;
|
||||
if (body.upload.quotas.perProject !== undefined) dbData.uploadQuotaPerProject = body.upload.quotas.perProject;
|
||||
}
|
||||
if (body.upload.storage) {
|
||||
if (body.upload.storage.type !== undefined) dbData.storageType = body.upload.storage.type;
|
||||
if (body.upload.storage.path !== undefined) dbData.storagePath = body.upload.storage.path;
|
||||
if (body.upload.storage.backupEnabled !== undefined) dbData.storageBackupEnabled = body.upload.storage.backupEnabled;
|
||||
if (body.upload.storage.compressionEnabled !== undefined) dbData.storageCompressionEnabled = body.upload.storage.compressionEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
// System Settings
|
||||
if (body.system) {
|
||||
if (body.system.timezone !== undefined) dbData.systemTimezone = body.system.timezone;
|
||||
if (body.system.backupSchedule !== undefined) dbData.systemBackupSchedule = body.system.backupSchedule;
|
||||
if (body.system.logLevel !== undefined) dbData.systemLogLevel = body.system.logLevel;
|
||||
if (body.system.maintenanceMode !== undefined) dbData.systemMaintenanceMode = body.system.maintenanceMode;
|
||||
if (body.system.autoUpdates !== undefined) dbData.systemAutoUpdates = body.system.autoUpdates;
|
||||
if (body.system.systemMonitoring !== undefined) dbData.systemMonitoring = body.system.systemMonitoring;
|
||||
if (body.system.performanceMetrics !== undefined) dbData.systemPerformanceMetrics = body.system.performanceMetrics;
|
||||
}
|
||||
|
||||
let settings;
|
||||
if (existingSettings) {
|
||||
// Update existing settings
|
||||
settings = await prisma.dms_settings.update({
|
||||
where: { settingID: existingSettings.settingID },
|
||||
data: dbData,
|
||||
});
|
||||
} else {
|
||||
// Create new settings
|
||||
settings = await prisma.dms_settings.create({
|
||||
data: {
|
||||
...dbData,
|
||||
settingCreatedDate: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "DMS settings updated successfully",
|
||||
data: { settingID: settings.settingID },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 405,
|
||||
message: "Method not allowed",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("DMS settings API error:", error);
|
||||
|
||||
// Provide more specific error messages
|
||||
if (error.code === 'P2002') {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Duplicate entry error",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
return {
|
||||
statusCode: 404,
|
||||
message: "Record not found",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Database error",
|
||||
error: error.message,
|
||||
code: error.code,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal server error",
|
||||
error: error.message,
|
||||
};
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user