diff --git a/package-lock.json b/package-lock.json index e45e8085f..44a4d9e39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@bimdata/bcf-components": "6.7.7", "@bimdata/components": "1.10.1", - "@bimdata/design-system": "2.3.0", + "@bimdata/design-system": "2.4.0", "@bimdata/typescript-fetch-api-client": "10.37.0", "@bimdata/viewer": "2.17.1", "@paddle/paddle-js": "^1.6.4", @@ -1876,9 +1876,9 @@ } }, "node_modules/@bimdata/design-system": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@bimdata/design-system/-/design-system-2.3.0.tgz", - "integrity": "sha512-/3vKXSptTUz2yaY4fcPCHu6fNeLYLqr03J4vQ8Tb8zKlIaUdgwcmFSTGM7bgXfyW6BlAkXPxp8wcEkcPnk2/Dw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@bimdata/design-system/-/design-system-2.4.0.tgz", + "integrity": "sha512-VUWTMUcfDDkmxEcRdOEwhOuWYg8wWam++ZGw5v5GHqhZeE6GuDBbd5dXL5tb0oVfo/5ArQmy2MV4BfGiAjfDOA==" }, "node_modules/@bimdata/typescript-fetch-api-client": { "version": "10.37.0", diff --git a/package.json b/package.json index 073717743..3e2eb083d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@bimdata/bcf-components": "6.7.7", "@bimdata/components": "1.10.1", - "@bimdata/design-system": "2.3.0", + "@bimdata/design-system": "2.4.0", "@bimdata/typescript-fetch-api-client": "10.37.0", "@bimdata/viewer": "2.17.1", "@paddle/paddle-js": "^1.6.4", diff --git a/src/components/specific/models/models-table/ModelsTable.vue b/src/components/specific/models/models-table/ModelsTable.vue index d35d88dd4..8ffa59722 100644 --- a/src/components/specific/models/models-table/ModelsTable.vue +++ b/src/components/specific/models/models-table/ModelsTable.vue @@ -42,7 +42,7 @@ - + @@ -102,7 +96,7 @@ export default { const hovering = ref(false); const showFullPath = ref(false); const hasLocation = computed( - () => ![MODEL_TYPE.META_BUILDING, MODEL_TYPE.PHOTOSPHERE_BUILDING].includes(props.model.type) + () => ![MODEL_TYPE.META_BUILDING, MODEL_TYPE.PHOTOSPHERE_BUILDING].includes(props.model.type), ); const folderLocation = (model) => { diff --git a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css new file mode 100644 index 000000000..0046cab3a --- /dev/null +++ b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css @@ -0,0 +1,22 @@ +.history-activity-panel { + height: calc(100vh - 44px - 40px - 12px - 12px); + overflow: auto; + .header { + h3 { + margin-bottom: 12px; + font-size: 18px; + } + } + .day-group { + margin-top: 20px; + .day-title { + font-size: 13px; + color: var(--color-granite); + margin-bottom: 8px; + font-weight: 600; + } + + } + +} + diff --git a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue new file mode 100644 index 000000000..569e49c91 --- /dev/null +++ b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.css b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.css new file mode 100644 index 000000000..c8316f8de --- /dev/null +++ b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.css @@ -0,0 +1,120 @@ +.activity-item { + display: flex; + border-radius: 12px; + padding: 8px; + margin-bottom: 6px; + border: 1px solid var(--color-silver-light); + transition: 0.2s; + cursor: pointer; + &:hover { + background: #f9fafb; + } + .icon { + width: 26px; + height: 26px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 12px; + font-size: 14px; + } + .content { + flex: 1; + .title { + font-size: 14px; + color: var(--color-granite); + .primary { + color: var(--color-primary); + font-weight: 500; + } + } + .date { + font-size: 12px; + color: #6b7280; + margin-top: 4px; + } + .details { + margin: 6px 0; + font-size: 11px; + color: var(--color-granite-light); + .detail { + min-height: 32px; + span { + color: var(--color-primary); + font-weight: 500; + } + } + } + .meta { + font-size: 12px; + color: #6b7280; + margin-top: 4px; + display: flex; + align-items: center; + gap: 6px; + .badge { + padding: 3px 8px; + border-radius: 50px; + font-size: 10px; + } + + .user { + background: #f3f4f6; + padding: 2px 6px; + border-radius: 999px; + font-size: 10px; + } + } + } + .arrow { + color: #9ca3af; + font-size: 18px; + } + .blue { + background: rgba(32, 93, 189, 0.1); + color: var(--color-neutral); + } + .flame { + background: rgba(255, 61, 30, .1); + color: var(--color-high); + } + .green { + background: rgba(0, 175, 80, .1); + color: var(--color-success); + } + .middle-blue { + background: rgba(0, 136, 224, 0.1); + color: #0088E0; + } + .orange { + background: rgba(255, 145, 0, 0.1); + color: var(--color-warning); + } + .pink { + background: rgba(227,85,119, 0.1); + color: #E35577; + } + .purple { + background: rgba(162,89,234, 0.1); + color: #A259EA; + } + .red { + background: rgba(188, 0, 10, 0.1); + color: var(--color-danger); + } + .teal { + background: rgba(27, 155, 162, 0.1); + color: #1B9BA2; + } + .expand-enter-active, + .expand-leave-active { + transition: all 0.2s ease; + } + + .expand-enter-from, + .expand-leave-to { + opacity: 0; + transform: translateY(-5px); + } +} \ No newline at end of file diff --git a/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.vue b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.vue new file mode 100644 index 000000000..78b8284bd --- /dev/null +++ b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/components/specific/projects/project-history-activity/activityConfig.js b/src/components/specific/projects/project-history-activity/activityConfig.js new file mode 100644 index 000000000..9bd6e15f2 --- /dev/null +++ b/src/components/specific/projects/project-history-activity/activityConfig.js @@ -0,0 +1,215 @@ +import { PROJECT_ROLE } from "../../../../config/projects.js"; +import { FILE_PERMISSION } from "../../../../config/files.js"; + +const roleList = { + [PROJECT_ROLE.ADMIN]: "admin", + [PROJECT_ROLE.USER]: "user", + [PROJECT_ROLE.GUEST]: "guest", +}; + +const PERMISSION_LIST = [ + { id: "default", value: FILE_PERMISSION.DEFAULT }, + { id: "accessDenied", value: FILE_PERMISSION.ACCESS_DENIED }, + { id: "readOnly", value: FILE_PERMISSION.READ_ONLY }, + { id: "readWrite", value: FILE_PERMISSION.READ_WRITE }, +]; + +const getFileName = (path) => path?.split("/").pop() || ""; + +const ACTION_CONFIG = { + document_created: { + actionKey: "ProjectOverview.activity.document_created", + getTarget: (log) => getFileName(log.description?.path), + icon: "DocumentCreated", + class: "blue", + badgeKey: "ProjectOverview.activity.badge.document_created", + }, + + document_deleted: { + actionKey: "ProjectOverview.activity.document_deleted", + getTarget: (log) => getFileName(log.description?.path), + icon: "DocumentDeleted", + class: "flame", + badgeKey: "ProjectOverview.activity.badge.document_deleted", + }, + + document_renamed: { + actionKey: "ProjectOverview.activity.document_renamed", + getTarget: (log) => getFileName(log.description?.old_path), + params: (log) => ({ + oldName: getFileName(log.description?.old_path), + newName: getFileName(log.description?.new_path), + }), + icon: "DocumentRenamed", + class: "purple", + badgeKey: "ProjectOverview.activity.badge.document_renamed", + }, + + document_moved: { + actionKey: "ProjectOverview.activity.document_moved", + getTarget: (log) => log.project_name, + icon: "DocumentMoved", + class: "teal", + badgeKey: "ProjectOverview.activity.badge.document_moved", + }, + + folder_created: { + actionKey: "ProjectOverview.activity.folder_created", + getTarget: (log) => getFileName(log.description?.path), + icon: "FolderCreated", + class: "blue", + badgeKey: "ProjectOverview.activity.badge.folder_created", + }, + + folder_deleted: { + actionKey: "ProjectOverview.activity.folder_deleted", + getTarget: (log) => getFileName(log.description?.path), + icon: "FolderDeleted", + class: "flame", + badgeKey: "ProjectOverview.activity.badge.folder_deleted", + }, + + folder_renamed: { + actionKey: "ProjectOverview.activity.folder_renamed", + getTarget: (log) => getFileName(log.description?.old_path), + params: (log) => ({ + oldName: getFileName(log.description?.old_path), + newName: getFileName(log.description?.new_path), + }), + icon: "FolderRenamed", + class: "purple", + badgeKey: "ProjectOverview.activity.badge.folder_renamed", + }, + + folder_moved: { + actionKey: "ProjectOverview.activity.folder_moved", + getTarget: (log) => log.description?.old_path, + params: (log) => ({ + oldPath: log.description?.old_path, + newPath: log.description?.new_path, + }), + icon: "FolderMoved", + class: "teal", + badgeKey: "ProjectOverview.activity.badge.folder_moved", + }, + + folder_permissions_updated: { + actionKey: "ProjectOverview.activity.folder_permissions_updated", + getTarget: (log) => getFileName(log.description?.path), + params: (log) => ({ + folderName: getFileName(log.description?.path), + }), + icon: "FolderPermissionsUpdated", + class: "pink", + badgeKey: "ProjectOverview.activity.badge.folder_permissions_updated", + }, + + cloud_invitation_sent: { + actionKey: "ProjectOverview.activity.cloud_invitation_sent", + getTarget: (log) => log.description?.email, + icon: "CloudInvitationSent", + class: "middle-blue", + badgeKey: "ProjectOverview.activity.badge.cloud_invitation_sent", + }, + + cloud_invitation_canceled: { + actionKey: "ProjectOverview.activity.cloud_invitation_canceled", + getTarget: (log) => log.description?.email, + icon: "CloudInvitationCancelled", + class: "orange", + badgeKey: "ProjectOverview.activity.badge.cloud_invitation_canceled", + }, + + cloud_invitation_accepted: { + actionKey: "ProjectOverview.activity.cloud_invitation_accepted", + getTarget: (log) => log.description?.email, + icon: "CloudInvitationAccepted", + class: "green", + badgeKey: "ProjectOverview.activity.badge.cloud_invitation_accepted", + }, + + cloud_invitation_denied: { + actionKey: "ProjectOverview.activity.cloud_invitation_denied", + getTarget: (log) => log.description?.email, + icon: "CloudInvitationDenied", + class: "red", + badgeKey: "ProjectOverview.activity.badge.cloud_invitation_denied", + }, + + project_invitation_sent: { + actionKey: "ProjectOverview.activity.project_invitation_sent", + getTarget: (log) => log.description?.invited_email, + icon: "ProjectInvitationSent", + class: "middle-blue", + badgeKey: "ProjectOverview.activity.badge.project_invitation_sent", + }, + + project_invitation_canceled: { + actionKey: "ProjectOverview.activity.project_invitation_canceled", + getTarget: (log) => log.description?.email, + icon: "ProjectInvitationCancelled", + class: "orange", + badgeKey: "ProjectOverview.activity.badge.project_invitation_canceled", + }, + + project_invitation_accepted: { + actionKey: "ProjectOverview.activity.project_invitation_accepted", + getTarget: (log) => log.user_email, + params: (log) => ({ + senderEmail: log.sender_email, + }), + icon: "ProjectInvitationAccepted", + class: "green", + badgeKey: "ProjectOverview.activity.badge.project_invitation_accepted", + }, + + project_invitation_denied: { + actionKey: "ProjectOverview.activity.project_invitation_denied", + getTarget: (log) => log.description?.email, + icon: "ProjectInvitationDenied", + class: "red", + badgeKey: "ProjectOverview.activity.badge.project_invitation_denied", + }, +}; + +export const getActivityFromLog = (log) => { + const config = ACTION_CONFIG[log.action] || ACTION_CONFIG.default; + + if (!config) { + return null; + } + + const roleKey = roleList[log.description?.project_role]; + + const newPermissionEntry = PERMISSION_LIST.find( + (entry) => entry.value === log.description?.new_permission, + ); + const oldPermissionEntry = PERMISSION_LIST.find( + (entry) => entry.value === log.description?.old_permission, + ); + + const newPermissionKey = newPermissionEntry ? newPermissionEntry.id : null; + const oldPermissionKey = oldPermissionEntry ? oldPermissionEntry.id : null; + + return { + actionKey: config.actionKey, + target: config.getTarget?.(log), + userEmail: log.description?.sender_email || log.user_email, + params: config.params?.(log), + icon: config.icon, + class: config.class, + badgeKey: config.badgeKey, + + details: { + path: log.description?.path, + folderId: log.description?.folder_id, + roleKey, + newPermissionKey, + oldPermissionKey, + oldName: getFileName(log.description?.old_path), + newName: getFileName(log.description?.new_path), + oldPath: log.description?.old_path, + newPath: log.description?.new_path, + }, + }; +}; diff --git a/src/components/specific/projects/project-notifications-settings/ProjectNotificationsSettings.vue b/src/components/specific/projects/project-notifications-settings/ProjectNotificationsSettings.vue index 0a62a95aa..69019b511 100644 --- a/src/components/specific/projects/project-notifications-settings/ProjectNotificationsSettings.vue +++ b/src/components/specific/projects/project-notifications-settings/ProjectNotificationsSettings.vue @@ -1,19 +1,5 @@