diff --git a/backend/src/rbac/record-sharing.controller.ts b/backend/src/rbac/record-sharing.controller.ts index 8df345d..5f5b7f0 100644 --- a/backend/src/rbac/record-sharing.controller.ts +++ b/backend/src/rbac/record-sharing.controller.ts @@ -154,8 +154,11 @@ export class RecordSharingController { canEdit: data.canEdit, canDelete: data.canDelete, }, - expiresAt: data.expiresAt || null, - }); + // Convert ISO string to MySQL datetime format + expiresAt: data.expiresAt + ? knex.raw('?', [new Date(data.expiresAt).toISOString().slice(0, 19).replace('T', ' ')]) + : null, + } as any); return RecordShare.query(knex) .findById(updated.id) @@ -163,7 +166,7 @@ export class RecordSharingController { } // Create new share - const share = await RecordShare.query(knex).insert({ + const share = await RecordShare.query(knex).insertAndFetch({ objectDefinitionId: objectDef.id, recordId, granteeUserId: data.granteeUserId, @@ -173,8 +176,11 @@ export class RecordSharingController { canEdit: data.canEdit, canDelete: data.canDelete, }, - expiresAt: data.expiresAt || null, - }); + // Convert ISO string to MySQL datetime format: YYYY-MM-DD HH:MM:SS + expiresAt: data.expiresAt + ? knex.raw('?', [new Date(data.expiresAt).toISOString().slice(0, 19).replace('T', ' ')]) + : null, + } as any); return RecordShare.query(knex) .findById(share.id) diff --git a/frontend/components/RecordSharing.vue b/frontend/components/RecordSharing.vue index 8519a7d..eb2a29a 100644 --- a/frontend/components/RecordSharing.vue +++ b/frontend/components/RecordSharing.vue @@ -146,12 +146,13 @@
- +
+ +
@@ -178,6 +179,7 @@ import { Input } from '~/components/ui/input'; import { Label } from '~/components/ui/label'; import { Badge } from '~/components/ui/badge'; import Checkbox from '~/components/ui/checkbox.vue'; +import DatePicker from '~/components/ui/date-picker/DatePicker.vue'; import { UserPlus, Trash2, Users } from 'lucide-vue-next'; interface Props { @@ -206,6 +208,24 @@ const newShare = ref({ expiresAt: '', }); +const expiresDate = ref(null); +const expiresTime = ref(''); + +// Computed property to combine date and time into ISO string +const combinedExpiresAt = computed(() => { + if (!expiresDate.value) return ''; + + const date = new Date(expiresDate.value); + if (expiresTime.value) { + const [hours, minutes] = expiresTime.value.split(':'); + date.setHours(parseInt(hours), parseInt(minutes), 0, 0); + } else { + date.setHours(23, 59, 59, 999); // Default to end of day + } + + return date.toISOString(); +}); + // Filter out users who already have shares const availableUsers = computed(() => { const sharedUserIds = new Set(shares.value.map(s => s.granteeUserId)); @@ -244,6 +264,10 @@ const loadUsers = async () => { const createShare = async () => { try { sharing.value = true; + + const expiresAtValue = combinedExpiresAt.value; + console.log('Creating share, expiresAt value:', expiresAtValue); + const payload: any = { granteeUserId: newShare.value.userId, canRead: newShare.value.canRead, @@ -252,10 +276,15 @@ const createShare = async () => { }; // Only include expiresAt if it has a value - if (newShare.value.expiresAt && newShare.value.expiresAt.trim()) { - payload.expiresAt = newShare.value.expiresAt; + if (expiresAtValue) { + payload.expiresAt = expiresAtValue; + console.log('Including expiresAt in payload:', payload.expiresAt); + } else { + console.log('Skipping expiresAt - no date selected'); } + console.log('Final payload:', payload); + await api.post( `/runtime/objects/${props.objectApiName}/records/${props.recordId}/shares`, payload @@ -269,6 +298,8 @@ const createShare = async () => { canDelete: false, expiresAt: '', }; + expiresDate.value = null; + expiresTime.value = ''; await loadShares(); } catch (e: any) { console.error('Failed to share record:', e);