add user validation in staing proccess and form

This commit is contained in:
firdausfazil 2025-07-15 10:56:46 +08:00
parent b5ee79339a
commit aa01c212ff
7 changed files with 886 additions and 25 deletions

View File

@ -0,0 +1,223 @@
# Form Access Control
## Overview
The form execution system now includes comprehensive access control that determines whether users can edit or only view forms based on their assignments and roles.
## How It Works
### Access Validation
When a user accesses a form in a process execution, the system checks:
1. **Direct Task Assignment**: If the task is directly assigned to the current user
2. **Process Definition Assignment**: If the form node in the process has specific user/role assignments
3. **Default Access**: If no specific assignment is found, defaults to public access
### Assignment Types
The system supports the following assignment types for forms:
#### 1. Public Assignment (`assignmentType: 'public'`)
- **Access**: Anyone can edit the form
- **Behavior**: Full edit access for all users
#### 2. User Assignment (`assignmentType: 'users'`)
- **Access**: Only specific users can edit the form
- **Check**: Current user ID or email must be in `assignedUsers` array
- **Example**:
```json
{
"assignmentType": "users",
"assignedUsers": [
{
"value": "123",
"label": "John Doe (john.doe)",
"username": "john.doe"
}
]
}
```
#### 3. Role Assignment (`assignmentType: 'roles'`)
- **Access**: Only users with specific roles can edit the form
- **Check**: Current user's roles must match `assignedRoles` array
- **Example**:
```json
{
"assignmentType": "roles",
"assignedRoles": [
{
"value": "2",
"label": "Manager"
}
]
}
```
#### 4. Variable Assignment (`assignmentType: 'variable'`)
- **Access**: Dynamic assignment based on process variables
- **Behavior**: Currently allows access (future enhancement needed)
## User Experience
### Edit Access
Users with edit access see:
- Green "Edit Access" badge
- Fully functional form inputs
- Submit buttons enabled
- Conditional logic active
### Read-only Access
Users without edit access see:
- Yellow "Read-only Access" badge with warning icon
- Disabled form inputs with gray styling
- Submit buttons disabled
- Clear explanation of why access is restricted
- Form data is visible but not editable
### Visual Indicators
#### Tab Navigation
- Warning icon next to form names for read-only forms
- Visual distinction between editable and read-only forms
#### Form Header
- Access status badges (Edit Access / Read-only Access)
- Detailed explanation for read-only access
- Color-coded indicators (green for edit, yellow for read-only)
#### Form Fields
- Disabled styling for read-only inputs
- Reduced opacity for entire form when disabled
- Cursor changes to "not-allowed" for disabled fields
## API Changes
### Enhanced Response
The `/api/cases/[id]/forms` endpoint now returns additional access control information:
```json
{
"forms": [
{
"formID": 123,
"formName": "Example Form",
"hasEditAccess": true,
"accessReason": "user_assigned",
"assignmentType": "users"
}
]
}
```
### Access Control Fields
- `hasEditAccess`: Boolean indicating if user can edit the form
- `accessReason`: String explaining the access decision
- `assignmentType`: The type of assignment configured for the form
## Security Considerations
### Authentication Required
- All form access requires valid authentication
- User context is validated on every request
### Role-based Validation
- User roles are fetched from database
- Role assignments are validated against current user's roles
### Assignment Validation
- Direct task assignments are checked first
- Process definition assignments are validated
- Fallback to public access if no assignment found
## Implementation Details
### Backend Changes
- Enhanced `/api/cases/[id]/forms` endpoint with access validation
- User role fetching and validation
- Assignment type checking logic
### Frontend Changes
- Readonly mode for forms without edit access
- Visual indicators for access status
- Disabled form submission for read-only forms
- Conditional logic disabled for read-only forms
### Form Behavior
- FormKit forms are disabled when user lacks edit access
- All form inputs are set to readonly/disabled
- Submit buttons are disabled
- Conditional logic scripts are not executed
## Configuration
### Setting Up Form Assignments
1. **Open Process Builder** - Navigate to the process you want to configure
2. **Select Form Node** - Click on the form node in your process
3. **Configure Assignment** - In the form configuration modal:
- Choose assignment type (Public, Users, Roles, or Variable)
- Select specific users or roles as needed
- Save the configuration
### Example Process Configuration
```json
{
"nodes": [
{
"id": "form-1",
"type": "form",
"data": {
"label": "Manager Approval Form",
"formId": "123",
"assignmentType": "roles",
"assignedRoles": [
{
"value": "2",
"label": "Manager"
},
{
"value": "3",
"label": "Supervisor"
}
]
}
}
]
}
```
## Troubleshooting
### No Access to Forms
If a user can't edit forms:
1. **Check User Roles** - Verify the user has the correct roles assigned
2. **Check Form Assignment** - Ensure the form node has proper assignment configuration
3. **Check Process Status** - Process must be published and not deleted
4. **Check Assignment Type** - Verify the assignment type is configured correctly
### Debug Information
The API endpoint includes console logging for debugging:
```javascript
// User information
console.log('Current user ID:', currentUser.userID);
console.log('User roles:', userRoleNames);
// Assignment checks
console.log('Checking form access:', {...});
console.log('Access result:', accessCheck);
```
## Future Enhancements
- Variable-based assignment evaluation
- Time-based access control
- Conditional access based on form data
- Audit logging for access attempts
- Advanced permission inheritance

View File

@ -0,0 +1,146 @@
# Assigned Processes Feature
## Overview
The "Start New Case" page now displays only processes where the current user is assigned to complete the first form task. This ensures users only see processes they have permission to start.
## How It Works
### API Endpoint
A new API endpoint `/api/process/assigned` has been created that:
1. **Authenticates the user** - Gets the current user from the request context
2. **Fetches user roles** - Retrieves all roles assigned to the current user
3. **Filters processes** - Only returns processes where the user is assigned to the first form
### Assignment Types
The system checks the assignment configuration of the first form node in each process:
#### 1. Public Assignment (`assignmentType: 'public'`)
- **Access**: Anyone can start the process
- **Behavior**: Process is included for all users
#### 2. User Assignment (`assignmentType: 'users'`)
- **Access**: Only specific users can start the process
- **Check**: Current user ID or email must be in `assignedUsers` array
- **Example**:
```json
{
"assignmentType": "users",
"assignedUsers": [
{
"value": "123",
"label": "John Doe (john.doe)",
"username": "john.doe"
}
]
}
```
#### 3. Role Assignment (`assignmentType: 'roles'`)
- **Access**: Only users with specific roles can start the process
- **Check**: Current user's roles must match `assignedRoles` array
- **Example**:
```json
{
"assignmentType": "roles",
"assignedRoles": [
{
"value": "2",
"label": "Manager"
}
]
}
```
#### 4. Variable Assignment (`assignmentType: 'variable'`)
- **Access**: Dynamic assignment based on process variables
- **Behavior**: Currently includes all processes (future enhancement needed)
### Frontend Changes
The `/execution/new-case` page has been updated to:
1. **Use the new API endpoint** - Calls `/api/process/assigned` instead of `/api/process`
2. **Updated UI** - Shows "My Assigned Processes" header and assignment indicators
3. **Better messaging** - Clear indication when no processes are assigned
## Configuration
### Setting Up Process Assignments
1. **Open Process Builder** - Navigate to the process you want to configure
2. **Select First Form Node** - Click on the first form node in your process
3. **Configure Assignment** - In the form configuration modal:
- Choose assignment type (Public, Users, Roles, or Variable)
- Select specific users or roles as needed
- Save the configuration
### Example Configuration
```json
{
"nodes": [
{
"id": "form-1",
"type": "form",
"data": {
"label": "Initial Request Form",
"formId": "123",
"assignmentType": "roles",
"assignedRoles": [
{
"value": "2",
"label": "Manager"
},
{
"value": "3",
"label": "Supervisor"
}
]
}
}
]
}
```
## Security Considerations
- **Authentication Required**: All requests must be authenticated
- **Role-based Access**: Users can only see processes they're assigned to
- **Audit Trail**: Process starts are logged with user information
## Troubleshooting
### No Processes Showing
If a user doesn't see any processes:
1. **Check User Roles** - Verify the user has the correct roles assigned
2. **Check Process Assignment** - Ensure the first form node has proper assignment configuration
3. **Check Process Status** - Process must be published and not deleted
4. **Check Assignment Type** - Verify the assignment type is configured correctly
### Debug Information
The API endpoint includes console logging for debugging:
```javascript
// User information
console.log('Current user ID:', currentUser.userID);
console.log('User roles:', userRoleNames);
// Assignment checks
console.log('Checking user assignment:', {...});
console.log('Checking role assignment:', {...});
console.log('Process assignment result:', isAssigned);
```
## Future Enhancements
1. **Variable Evaluation** - Implement proper variable-based assignment
2. **Multiple Form Support** - Check assignments across multiple form nodes
3. **Permission Inheritance** - Support for inherited permissions from parent processes
4. **Bulk Assignment** - Tools for bulk assigning processes to users/roles

View File

@ -25,7 +25,18 @@
:class="{ 'bg-primary text-white': activeTabIndex === index }">
{{ index + 1 }}
</div>
{{ form.formName || `Form ${index + 1}` }}
<div class="flex items-center">
{{ form.formName || `Form ${index + 1}` }}
<!-- Access indicator -->
<div v-if="!form.hasEditAccess" class="ml-2">
<div class="w-4 h-4 rounded-full bg-yellow-100 flex items-center justify-center"
title="Read-only access">
<svg class="w-3 h-3 text-yellow-600" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
</div>
</div>
</div>
</button>
</nav>
@ -34,8 +45,49 @@
<!-- Tab Content -->
<div class="bg-white rounded-lg shadow p-6">
<div v-for="(form, index) in forms" :key="`content-${index}`" v-show="activeTabIndex === index">
<h2 class="text-xl font-semibold mb-4">{{ form.formName || `Form ${index + 1}` }}</h2>
<p class="text-gray-600 mb-1">{{ form.description || 'Please complete this form step' }}</p>
<!-- Form Header with Access Status -->
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<h2 class="text-xl font-semibold">{{ form.formName || `Form ${index + 1}` }}</h2>
<!-- Access Status Badge -->
<div v-if="!form.hasEditAccess" class="flex items-center">
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800 border border-yellow-200">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
Read-only Access
</div>
</div>
<div v-else class="flex items-center">
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800 border border-green-200">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
Edit Access
</div>
</div>
</div>
<p class="text-gray-600 mb-1">{{ form.description || 'Please complete this form step' }}</p>
<!-- Access Information -->
<div v-if="!form.hasEditAccess" class="mt-3 p-3 bg-yellow-50 border border-yellow-200 rounded-md">
<div class="flex items-start">
<svg class="w-5 h-5 text-yellow-600 mr-2 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
<div>
<p class="text-sm font-medium text-yellow-800">Read-only Access</p>
<p class="text-sm text-yellow-700 mt-1">
You can view this form but cannot make changes.
<span v-if="form.accessReason === 'user_not_assigned'">This form is assigned to specific users.</span>
<span v-else-if="form.accessReason === 'role_not_assigned'">This form is assigned to specific roles.</span>
<span v-else>You don't have permission to edit this form.</span>
</p>
</div>
</div>
</div>
</div>
<!-- Conditional Logic Engine -->
<ConditionalLogicEngine
@ -53,6 +105,7 @@
:actions="false"
:incomplete-message="false"
validation-visibility="submit"
:disabled="!form.hasEditAccess"
>
<div class="grid-preview-container">
<template v-if="form.formComponents && form.formComponents.length > 0">
@ -76,6 +129,8 @@
:value="component.props?.value"
:class="component.props?.width ? `w-${component.props.width}` : 'w-full'"
class="mb-4"
:disabled="!form.hasEditAccess"
:readonly="!form.hasEditAccess"
/>
<!-- Heading -->
@ -136,7 +191,7 @@
:type="component.props?.buttonType || 'button'"
:variant="component.props?.variant || 'primary'"
:size="component.props?.size || 'md'"
:disabled="component.props?.disabled || false"
:disabled="component.props?.disabled || !form.hasEditAccess"
>
{{ component.props?.label || 'Button' }}
</RsButton>
@ -153,7 +208,7 @@
v-if="!hasSubmitButton(form)"
type="submit"
label="Submit"
:disabled="submitting"
:disabled="submitting || !form.hasEditAccess"
:classes="{
input: submitting ? 'opacity-75 cursor-wait' : ''
}"
@ -253,6 +308,9 @@
}
const showField = (fieldName) => {
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't modify fields if readonly
const field = document.querySelector(`[name="${fieldName}"]`)
if (field) {
const outerDiv = field.closest('.formkit-outer')
@ -264,6 +322,9 @@
}
const hideField = (fieldName) => {
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't modify fields if readonly
const field = document.querySelector(`[name="${fieldName}"]`)
if (field) {
const outerDiv = field.closest('.formkit-outer')
@ -275,6 +336,9 @@
}
const enableField = (fieldName) => {
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't modify fields if readonly
const field = document.querySelector(`[name="${fieldName}"]`)
if (field) {
field.removeAttribute('disabled')
@ -286,6 +350,9 @@
}
const disableField = (fieldName) => {
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't modify fields if readonly
const field = document.querySelector(`[name="${fieldName}"]`)
if (field) {
field.setAttribute('disabled', 'disabled')
@ -297,6 +364,9 @@
}
const onFieldChange = (fieldName, callback) => {
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't add listeners if readonly
const field = document.querySelector(`[name="${fieldName}"]`)
if (field) {
// Remove existing listeners first to prevent duplicates
@ -313,6 +383,9 @@
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.formComponents) return
// Don't apply conditional logic if user doesn't have edit access
if (!currentForm.hasEditAccess) return
currentForm.formComponents.forEach(component => {
if (component.props?.conditionalLogic?.enabled) {
const { conditions, action, operator = 'and' } = component.props.conditionalLogic
@ -392,6 +465,9 @@
const handleScriptGenerated = (script) => {
if (!script) return
const currentForm = forms.value[activeTabIndex.value]
if (!currentForm?.hasEditAccess) return // Don't execute scripts if readonly
try {
// Create a function with access to our utility functions
const evalScript = new Function(
@ -421,6 +497,13 @@
// Handle form submission
const handleSubmit = async (formIndex) => {
try {
// Check if user has edit access to this form
const currentForm = forms.value[formIndex];
if (!currentForm.hasEditAccess) {
error.value = 'You do not have permission to submit this form';
return;
}
submitting.value = true
console.log(`Form ${formIndex + 1} submitted:`, formData.value[formIndex])
@ -550,4 +633,40 @@
color: #6b7280;
margin-top: 0.25rem;
}
/* Readonly form styling */
:deep(.formkit-form[disabled]) {
opacity: 0.7;
pointer-events: none;
}
:deep(.formkit-form[disabled] .formkit-input) {
background-color: #f9fafb;
border-color: #e5e7eb;
color: #6b7280;
cursor: not-allowed;
}
:deep(.formkit-form[disabled] .formkit-label) {
color: #6b7280;
}
:deep(.formkit-form[disabled] .formkit-help) {
color: #9ca3af;
}
/* Readonly input styling */
:deep(.formkit-input[readonly]) {
background-color: #f9fafb;
border-color: #e5e7eb;
color: #6b7280;
cursor: not-allowed;
}
:deep(.formkit-input[disabled]) {
background-color: #f9fafb;
border-color: #e5e7eb;
color: #6b7280;
cursor: not-allowed;
}
</style>

View File

@ -2,6 +2,19 @@
<div>
<LayoutsBreadcrumb />
<!-- Page Header -->
<rs-card class="mb-6">
<div class="p-5">
<div class="flex items-center mb-4">
<Icon name="material-symbols:assignment" class="text-blue-600 w-6 h-6 mr-3" />
<h1 class="text-xl font-semibold text-gray-900">My Assigned Processes</h1>
</div>
<p class="text-gray-600 text-sm">
Start a new case from processes where you are assigned to complete the first form task.
</p>
</div>
</rs-card>
<!-- Search and Filter -->
<rs-card>
<div class="p-5 flex flex-wrap gap-4">
@ -9,7 +22,7 @@
<FormKit
type="text"
name="search"
placeholder="Search processes..."
placeholder="Search assigned processes..."
prefix-icon="search"
v-model="searchQuery"
/>
@ -69,13 +82,20 @@
></Icon>
<span>Created: {{ formatDate(process.createdAt) }}</span>
</div>
<div class="flex items-center">
<div class="flex items-center mb-1">
<Icon
class="text-base mr-1"
name="material-symbols:sync"
></Icon>
<span>Status: {{ process.status }}</span>
</div>
<div class="flex items-center">
<Icon
class="text-base mr-1"
name="material-symbols:assignment"
></Icon>
<span class="text-blue-600 font-medium">Assigned to you</span>
</div>
</div>
<div class="mt-4">
<rs-button
@ -92,9 +112,9 @@
<!-- Empty State -->
<div v-if="filteredProcesses.length === 0 && !loading" class="col-span-3 flex flex-col items-center justify-center py-12 text-gray-500">
<Icon name="material-symbols:category-outline" class="w-16 h-16 mb-4 text-gray-300" />
<p class="text-base font-medium">No processes found</p>
<p class="text-sm mt-1">Try selecting a different category or search term</p>
<Icon name="material-symbols:assignment-outline" class="w-16 h-16 mb-4 text-gray-300" />
<p class="text-base font-medium">No assigned processes found</p>
<p class="text-sm mt-1">You don't have any processes assigned to you. Contact your administrator to get access to processes.</p>
</div>
</div>
</div>
@ -105,7 +125,7 @@ import { ref, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
definePageMeta({
title: "Start New Case",
title: "Start New Case - Assigned Processes",
layout: "default",
middleware: ["auth"],
requiresAuth: true,
@ -127,8 +147,8 @@ const fetchProcesses = async () => {
loading.value = true;
error.value = null;
// Only fetch published processes that are not templates
const response = await $fetch('/api/process', {
// Fetch processes that the current user is assigned to in the first form
const response = await $fetch('/api/process/assigned', {
params: {
status: 'published',
isTemplate: false

View File

@ -15,6 +15,16 @@ export default defineEventHandler(async (event) => {
};
}
// Get the current authenticated user from the request context
const currentUser = event.context.user;
if (!currentUser || !currentUser.userID) {
return {
success: false,
error: 'Authentication required'
};
}
// Check if the ID is a UUID or numeric ID
const isUUID = caseId.length === 36 && caseId.includes('-');
@ -29,7 +39,8 @@ export default defineEventHandler(async (event) => {
processID: true,
processUUID: true,
processName: true,
processDescription: true
processDescription: true,
processDefinition: true
}
},
startedBy: {
@ -88,12 +99,95 @@ export default defineEventHandler(async (event) => {
};
}
// Get current user's roles for access validation
const userRoles = await prisma.userrole.findMany({
where: {
userRoleUserID: parseInt(currentUser.userID)
},
select: {
role: {
select: {
roleID: true,
roleName: true
}
}
}
});
const userRoleIds = userRoles.map(ur => ur.role.roleID);
const userRoleNames = userRoles.map(ur => ur.role.roleName);
// Function to check if user has access to a form
const checkFormAccess = (task, processDefinition) => {
// If task is directly assigned to current user, they have access
if (task.assignedTo && task.assignedTo.userID === parseInt(currentUser.userID)) {
return { hasAccess: true, reason: 'directly_assigned' };
}
// Check process definition for form node assignment
const nodes = processDefinition?.nodes || [];
const formNodes = nodes.filter(node => node.type === 'form');
// Find the form node that corresponds to this task
const formNode = formNodes.find(node => {
const nodeData = node.data || {};
return nodeData.formId === task.form?.formID || nodeData.formUuid === task.form?.formUUID;
});
if (!formNode) {
// If no form node found, default to public access
return { hasAccess: true, reason: 'public_default' };
}
const formData = formNode.data || {};
const assignmentType = formData.assignmentType || 'public';
// Check assignment type
if (assignmentType === 'public') {
return { hasAccess: true, reason: 'public_assignment' };
} else if (assignmentType === 'users') {
const assignedUsers = formData.assignedUsers || [];
const currentUserIdStr = String(currentUser.userID);
const hasUserAccess = assignedUsers.some(user =>
String(user.value) === currentUserIdStr ||
user.username === currentUser.email
);
return {
hasAccess: hasUserAccess,
reason: hasUserAccess ? 'user_assigned' : 'user_not_assigned'
};
} else if (assignmentType === 'roles') {
const assignedRoles = formData.assignedRoles || [];
const hasRoleAccess = assignedRoles.some(role =>
userRoleIds.includes(parseInt(role.value)) ||
userRoleNames.includes(role.label)
);
return {
hasAccess: hasRoleAccess,
reason: hasRoleAccess ? 'role_assigned' : 'role_not_assigned'
};
} else if (assignmentType === 'variable') {
// For variable-based assignment, we'll allow access for now
// In a real implementation, you might want to evaluate the variable
return { hasAccess: true, reason: 'variable_assignment' };
}
return { hasAccess: false, reason: 'no_access' };
};
// Extract forms from tasks and remove duplicates
const forms = [];
const formIds = new Set();
for (const task of caseInstance.tasks) {
if (task.form) {
// Check if user has access to this form
const accessCheck = checkFormAccess(task, caseInstance.process.processDefinition);
// Make sure formComponents is properly structured
let formComponents = [];
try {
@ -136,7 +230,14 @@ export default defineEventHandler(async (event) => {
taskData: task.taskData,
formData: formData,
submittedAt: task.taskData?.submittedAt || null,
completedDate: task.taskCompletedDate
completedDate: task.taskCompletedDate,
// Add access control information
hasEditAccess: accessCheck.hasAccess,
accessReason: accessCheck.reason,
assignmentType: accessCheck.reason === 'public_assignment' ? 'public' :
accessCheck.reason === 'user_assigned' ? 'users' :
accessCheck.reason === 'role_assigned' ? 'roles' :
accessCheck.reason === 'variable_assignment' ? 'variable' : 'none'
});
}
}

View File

@ -57,12 +57,36 @@ export default defineEventHandler(async (event) => {
};
}
// Get the current user (in a real app, this would come from the authenticated user)
const currentUser = {
userID: 1, // This would be the actual user ID in a real app
userFullName: 'John Doe',
userUsername: 'johndoe'
};
// Get the current authenticated user from the request context
const currentUser = event.context.user;
if (!currentUser || !currentUser.userID) {
console.log('User not authenticated');
return {
success: false,
error: 'Authentication required'
};
}
// Get full user details from database
const userDetails = await prisma.user.findFirst({
where: {
userID: parseInt(currentUser.userID)
},
select: {
userID: true,
userFullName: true,
userUsername: true
}
});
if (!userDetails) {
console.log('User details not found');
return {
success: false,
error: 'User not found'
};
}
console.log('Creating case instance...');
// Create a new case instance
@ -72,7 +96,7 @@ export default defineEventHandler(async (event) => {
processID: process.processID,
caseName: `${process.processName} - ${new Date().toLocaleDateString()}`,
caseStatus: 'active',
caseStartedBy: currentUser.userID,
caseStartedBy: userDetails.userID,
caseVariables: process.processVariables || {},
caseSettings: process.processSettings || {},
caseDefinition: process.processDefinition || {},
@ -112,7 +136,7 @@ export default defineEventHandler(async (event) => {
taskName: formNode.data?.label || 'Complete Form',
taskType: 'form',
taskStatus: 'pending',
taskAssignedTo: currentUser.userID,
taskAssignedTo: userDetails.userID,
taskFormID: formNode.data?.formId,
taskCreatedDate: new Date(),
taskModifiedDate: new Date()
@ -128,9 +152,9 @@ export default defineEventHandler(async (event) => {
data: {
caseID: caseInstance.caseID,
timelineType: 'start',
timelineDescription: `Process started by ${currentUser.userFullName}`,
timelineDescription: `Process started by ${userDetails.userFullName}`,
timelineDate: new Date(),
timelineCreatedBy: currentUser.userID
timelineCreatedBy: userDetails.userID
}
});

View File

@ -0,0 +1,228 @@
import { PrismaClient } from '@prisma/client';
// Initialize Prisma client
const prisma = new PrismaClient();
export default defineEventHandler(async (event) => {
try {
// Get the current authenticated user from the request context
const currentUser = event.context.user;
if (!currentUser || !currentUser.userID) {
return {
success: false,
error: 'Authentication required'
};
}
// Get query parameters
const query = getQuery(event);
const {
page = 1,
limit = 20,
status,
category,
search,
isTemplate,
sortBy = 'processCreatedDate',
sortOrder = 'desc'
} = query;
// Build where clause
const where = {};
// Exclude deleted processes by default unless explicitly requested
if (query.includeDeleted !== 'true') {
where.processStatus = { not: 'deleted' };
}
if (status && status !== 'deleted') {
// If status filter is provided and it's not 'deleted', filter by that status
// and still exclude deleted processes
where.processStatus = status;
} else if (status === 'deleted') {
// If specifically requesting deleted processes, only show those
where.processStatus = 'deleted';
}
if (category) {
where.processCategory = category;
}
if (isTemplate !== undefined) {
where.isTemplate = isTemplate === 'true';
}
if (search) {
where.OR = [
{ processName: { contains: search, mode: 'insensitive' } },
{ processDescription: { contains: search, mode: 'insensitive' } }
];
}
// Calculate pagination
const skip = (parseInt(page) - 1) * parseInt(limit);
const take = parseInt(limit);
// Build orderBy clause
const orderBy = {};
orderBy[sortBy] = sortOrder;
// Get all processes first
const allProcesses = await prisma.process.findMany({
where,
orderBy,
skip,
take,
select: {
processID: true,
processUUID: true,
processName: true,
processDescription: true,
processCategory: true,
processPriority: true,
processOwner: true,
processVersion: true,
processStatus: true,
isTemplate: true,
templateCategory: true,
processCreatedDate: true,
processModifiedDate: true,
processDefinition: true,
creator: {
select: {
userID: true,
userFullName: true,
userUsername: true
}
}
}
});
// Get current user's roles
const userRoles = await prisma.userrole.findMany({
where: {
userRoleUserID: parseInt(currentUser.userID)
},
select: {
role: {
select: {
roleID: true,
roleName: true
}
}
}
});
const userRoleIds = userRoles.map(ur => ur.role.roleID);
const userRoleNames = userRoles.map(ur => ur.role.roleName);
// Filter processes based on user assignments in the first form
const filteredProcesses = [];
for (const process of allProcesses) {
const processDefinition = process.processDefinition || {};
const nodes = processDefinition.nodes || [];
const edges = processDefinition.edges || [];
// Find all form nodes
const formNodes = nodes.filter(node => node.type === 'form');
if (formNodes.length === 0) {
// If no form nodes, skip this process
continue;
}
// Get the first form node (assuming it's the starting form)
const firstFormNode = formNodes[0];
const firstFormData = firstFormNode.data || {};
// Check if the user is assigned to this form
let isAssigned = false;
// Check assignment type
const assignmentType = firstFormData.assignmentType || 'public';
if (assignmentType === 'public') {
// Public assignment - anyone can access
isAssigned = true;
} else if (assignmentType === 'users') {
// Check if current user is in assigned users
const assignedUsers = firstFormData.assignedUsers || [];
const currentUserIdStr = String(currentUser.userID);
isAssigned = assignedUsers.some(user =>
String(user.value) === currentUserIdStr ||
user.username === currentUser.email
);
} else if (assignmentType === 'roles') {
// Check if current user's roles are in assigned roles
const assignedRoles = firstFormData.assignedRoles || [];
isAssigned = assignedRoles.some(role =>
userRoleIds.includes(parseInt(role.value)) ||
userRoleNames.includes(role.label)
);
} else if (assignmentType === 'variable') {
// For variable-based assignment, we'll include it for now
// In a real implementation, you might want to evaluate the variable
isAssigned = true;
}
if (isAssigned) {
filteredProcesses.push(process);
}
}
// Count total processes for pagination (we need to get all processes to filter)
const totalProcesses = await prisma.process.count({ where });
// Calculate pagination info
const totalPages = Math.ceil(totalProcesses / take);
const hasNextPage = parseInt(page) < totalPages;
const hasPrevPage = parseInt(page) > 1;
return {
success: true,
data: {
processes: filteredProcesses.map(process => ({
processID: process.processID,
processUUID: process.processUUID,
processName: process.processName,
processDescription: process.processDescription,
processCategory: process.processCategory,
processPriority: process.processPriority,
processOwner: process.processOwner,
processVersion: process.processVersion,
processStatus: process.processStatus,
isTemplate: process.isTemplate,
templateCategory: process.templateCategory,
processCreatedDate: process.processCreatedDate,
processModifiedDate: process.processModifiedDate,
creator: process.creator
})),
pagination: {
currentPage: parseInt(page),
totalPages,
totalCount: totalProcesses,
filteredCount: filteredProcesses.length,
limit: take,
hasNextPage,
hasPrevPage
}
}
};
} catch (error) {
console.error('Error fetching assigned processes:', error);
return {
success: false,
error: 'Failed to fetch assigned processes',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
};
}
});