Compare commits
22 Commits
feature/sa
...
c50098a55c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c50098a55c | ||
|
|
e73126bcb7 | ||
|
|
6c29d18696 | ||
|
|
3fbc019083 | ||
|
|
3086f78d34 | ||
|
|
d15fc918d1 | ||
|
|
56c0c3838d | ||
|
|
9ac69e30d0 | ||
|
|
d37183ba45 | ||
|
|
b4bdeeb9f6 | ||
|
|
f4143ab106 | ||
|
|
516e132611 | ||
|
|
c5305490c1 | ||
|
|
4520f94b69 | ||
|
|
e4f1ba96ad | ||
|
|
52c0849de2 | ||
|
|
b9fa3bd008 | ||
|
|
2bc672e4c5 | ||
|
|
962c84e6d2 | ||
|
|
fc1bec4de7 | ||
|
|
0275b96014 | ||
|
|
e4f3bad971 |
@@ -21,13 +21,13 @@ export class RecordShare extends BaseModel {
|
||||
};
|
||||
}
|
||||
|
||||
// Don't auto-set timestamps - let DB defaults handle them
|
||||
$beforeInsert() {
|
||||
// Don't call super - skip BaseModel's timestamp logic
|
||||
// Override BaseModel hooks to prevent automatic timestamp handling
|
||||
$beforeInsert(queryContext: any) {
|
||||
// Don't set timestamps - let database defaults handle it
|
||||
}
|
||||
|
||||
$beforeUpdate() {
|
||||
// Don't call super - skip BaseModel's timestamp logic
|
||||
$beforeUpdate(opt: any, queryContext: any) {
|
||||
// Don't set timestamps - let database defaults handle it
|
||||
}
|
||||
|
||||
id!: string;
|
||||
|
||||
@@ -147,43 +147,39 @@ export class RecordSharingController {
|
||||
|
||||
if (existingShare) {
|
||||
// Update existing share
|
||||
const updated = await RecordShare.query(knex)
|
||||
.patchAndFetchById(existingShare.id, {
|
||||
accessLevel: {
|
||||
await knex('record_shares')
|
||||
.where({ id: existingShare.id })
|
||||
.update({
|
||||
accessLevel: JSON.stringify({
|
||||
canRead: data.canRead,
|
||||
canEdit: data.canEdit,
|
||||
canDelete: data.canDelete,
|
||||
},
|
||||
// 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);
|
||||
}),
|
||||
expiresAt: data.expiresAt ? data.expiresAt : null,
|
||||
updatedAt: knex.fn.now(),
|
||||
});
|
||||
|
||||
return RecordShare.query(knex)
|
||||
.findById(updated.id)
|
||||
.findById(existingShare.id)
|
||||
.withGraphFetched('[granteeUser]');
|
||||
}
|
||||
|
||||
// Create new share
|
||||
const share = await RecordShare.query(knex).insertAndFetch({
|
||||
const [shareId] = await knex('record_shares').insert({
|
||||
objectDefinitionId: objectDef.id,
|
||||
recordId,
|
||||
granteeUserId: data.granteeUserId,
|
||||
grantedByUserId: currentUser.userId,
|
||||
accessLevel: {
|
||||
accessLevel: JSON.stringify({
|
||||
canRead: data.canRead,
|
||||
canEdit: data.canEdit,
|
||||
canDelete: data.canDelete,
|
||||
},
|
||||
// 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);
|
||||
}),
|
||||
expiresAt: data.expiresAt ? data.expiresAt : null,
|
||||
});
|
||||
|
||||
return RecordShare.query(knex)
|
||||
.findById(share.id)
|
||||
.findById(shareId)
|
||||
.withGraphFetched('[granteeUser]');
|
||||
}
|
||||
|
||||
@@ -239,9 +235,11 @@ export class RecordSharingController {
|
||||
}
|
||||
|
||||
// Revoke the share (soft delete)
|
||||
await RecordShare.query(knex)
|
||||
.patchAndFetchById(shareId, {
|
||||
revokedAt: knex.fn.now() as any,
|
||||
await knex('record_shares')
|
||||
.where({ id: shareId })
|
||||
.update({
|
||||
revokedAt: knex.fn.now(),
|
||||
updatedAt: knex.fn.now(),
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
|
||||
@@ -146,15 +146,14 @@
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="expiresAt">Expires At (Optional)</Label>
|
||||
<div class="flex gap-2">
|
||||
<DatePicker
|
||||
v-model="expiresDate"
|
||||
placeholder="Select date"
|
||||
class="flex-1"
|
||||
<Input
|
||||
id="expiresAt"
|
||||
v-model="newShare.expiresAt"
|
||||
type="datetime-local"
|
||||
placeholder="Never"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="showShareDialog = false">Cancel</Button>
|
||||
<Button
|
||||
@@ -179,7 +178,6 @@ 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 {
|
||||
@@ -208,24 +206,6 @@ const newShare = ref({
|
||||
expiresAt: '',
|
||||
});
|
||||
|
||||
const expiresDate = ref<Date | null>(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));
|
||||
@@ -264,10 +244,6 @@ 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,
|
||||
@@ -276,15 +252,10 @@ const createShare = async () => {
|
||||
};
|
||||
|
||||
// Only include expiresAt if it has a value
|
||||
if (expiresAtValue) {
|
||||
payload.expiresAt = expiresAtValue;
|
||||
console.log('Including expiresAt in payload:', payload.expiresAt);
|
||||
} else {
|
||||
console.log('Skipping expiresAt - no date selected');
|
||||
if (newShare.value.expiresAt && newShare.value.expiresAt.trim()) {
|
||||
payload.expiresAt = newShare.value.expiresAt;
|
||||
}
|
||||
|
||||
console.log('Final payload:', payload);
|
||||
|
||||
await api.post(
|
||||
`/runtime/objects/${props.objectApiName}/records/${props.recordId}/shares`,
|
||||
payload
|
||||
@@ -298,8 +269,6 @@ const createShare = async () => {
|
||||
canDelete: false,
|
||||
expiresAt: '',
|
||||
};
|
||||
expiresDate.value = null;
|
||||
expiresTime.value = '';
|
||||
await loadShares();
|
||||
} catch (e: any) {
|
||||
console.error('Failed to share record:', e);
|
||||
|
||||
Reference in New Issue
Block a user