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);