diff --git a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css
index e511e667..d93ca242 100644
--- a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css
+++ b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.css
@@ -1,23 +1,31 @@
.history-activity-panel {
- height: calc(100vh - 44px - 40px - 12px - 12px);
- overflow: auto;
+ height: calc(100% - 40px);
+ display: flex;
+ flex-direction: column;
.header {
h3 {
margin-bottom: 12px;
font-size: 18px;
}
}
- .input-search {
- border: 1px solid transparent;
- border-radius: 3px;
- &.focus {
- background-color: var(--color-silver-light);
- box-shadow: none;
- border: 1px solid #efefef;
+ .actions {
+ gap: 6px;
+ .input-search {
+ border: 1px solid transparent;
+ border-radius: 3px;
+ &.focus {
+ background-color: var(--color-silver-light);
+ box-shadow: none;
+ border: 1px solid #efefef;
+ }
}
}
+ .content {
+ flex: 1;
+ overflow: auto;
+ }
.day-group {
- margin-top: 20px;
+ margin-top: var(--spacing-unit);
.day-title {
font-size: 13px;
color: var(--color-granite);
diff --git a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue
index 27fb9966..eb7ebc56 100644
--- a/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue
+++ b/src/components/specific/projects/project-history-activity/ProjectHistoryActivity.vue
@@ -1,33 +1,44 @@
-
+
+
-
-
-
-
-
-
- {{ $t("ProjectOverview.activity.empty") }}
-
+
+
+
+
+
+
+
+ {{ $t("ProjectOverview.activity.empty") }}
+
+
@@ -40,6 +51,7 @@ import { useTimeAgo } from "../../../../composables/time.js";
import ProjectService from "../../../../services/ProjectService.js";
import ActivityItem from "./activity-item/ActivityItem.vue";
+import ActivityFilters from "./activity-filters/ActivityFilters.vue";
export default {
props: {
@@ -47,6 +59,7 @@ export default {
},
components: {
ActivityItem,
+ ActivityFilters,
},
emits: ["go-folder"],
@@ -119,24 +132,58 @@ export default {
const displayedGroupedLogs = computed(() => {
const search = searchText.value.trim().toLowerCase();
+ const f = filters.value;
return sortedLogs.value
- .filter((log) => !search || log._search.includes(search))
+ .filter((log) => {
+ if (search && !log._search.includes(search)) return false;
+
+ // Types
+ if (f.types.length && !f.types.includes(log.action)) return false;
+
+ // Users
+ if (f.users.length && !f.users.includes(log.user_email)) return false;
+
+ // Date
+ if (f.dateFrom && log.dateObj < f.dateFrom) return false;
+ if (f.dateTo) {
+ const endOfDay = new Date(f.dateTo);
+ endOfDay.setHours(23, 59, 59, 999);
+ if (log.dateObj > endOfDay) return false;
+ }
+
+ return true;
+ })
.reduce((groups, log) => {
const day = formatDay(log.dateObj);
-
groups[day] ??= [];
groups[day].push(log);
-
return groups;
}, {});
});
+
const hasDisplayedLogs = computed(() => Object.keys(displayedGroupedLogs.value).length > 0);
+ const filters = ref({
+ types: [],
+ users: [],
+ dateFrom: null,
+ dateTo: null,
+ });
+
+ const availableUsers = computed(() => [
+ ...new Set(logs.value.map((l) => l.user_email).filter(Boolean)),
+ ]);
+
+ const availableActions = computed(() => new Set(logs.value.map((l) => l.action)));
+
return {
displayedGroupedLogs,
hasDisplayedLogs,
searchText,
+ filters,
+ availableUsers,
+ availableActions,
formatTimeAgo,
};
},
diff --git a/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.css b/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.css
new file mode 100644
index 00000000..f5c2470a
--- /dev/null
+++ b/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.css
@@ -0,0 +1,87 @@
+.activity-filters {
+ position: relative;
+
+ /* Filters button */
+ .activity-filters__btn {
+ gap: 6px;
+ .activity-filters__btn--active {
+ border-color: var(--color-primary);
+ background-color: rgba(47, 55, 74, .05);
+ color: var(--color-primary);
+ }
+ .activity-filters__badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 18px;
+ height: 18px;
+ padding: 0 4px;
+ border-radius: 9px;
+ background: var(--color-primary);
+ color: white;
+ font-size: 10px;
+ font-weight: 600;
+ }
+ }
+
+ /* Panel */
+ .activity-filters__panel {
+ position: absolute;
+ top: calc(100% + 6px);
+ right: 3px;
+ z-index: 100;
+ width: 320px;
+ background: var(--color-white);
+ border-radius: 6px;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+ overflow: auto;
+ .activity-filters__panel-header {
+ padding: 12px 16px;
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--color-primary);
+ border-bottom: 1px solid var(--color-silver-light, #f0f0f0);
+ }
+
+ /* Sections */
+ .activity-filters__section {
+ padding: 12px;
+ .activity-filters__title {
+ font-size: 11px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ margin-bottom: 3px;
+ }
+ .activity-filters__row {
+ padding: 2px 0;
+ font-size: 12px;
+ cursor: pointer;
+ user-select: none;
+
+ &.is-disabled {
+ opacity: 0.4;
+ cursor: default;
+ }
+ }
+ }
+ .activity-filters__date-range {
+ :deep() .not-empty {
+ label {
+ display: none
+ }
+ }
+ }
+
+ /* Footer */
+ .activity-filters__footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: var(--spacing-unit);
+ padding: 12px 16px;
+ border-top: 1px solid var(--color-silver-light, #f0f0f0);
+ background: var(--color-white);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.vue b/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.vue
new file mode 100644
index 00000000..19f5f7cd
--- /dev/null
+++ b/src/components/specific/projects/project-history-activity/activity-filters/ActivityFilters.vue
@@ -0,0 +1,232 @@
+
+
+
+
+ {{ $t("t.filters") }}
+
+ {{ activeFilterCount }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("ProjectOverview.activity.filters.types") }}
+
+
+
+
+
+
+
+
+
+ {{ $t("ProjectOverview.activity.filters.users") }}
+
+
+
+
+
+
+
+
+
+ {{ $t("ProjectOverview.activity.filters.period") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
index 86b8c403..df63f171 100644
--- a/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.css
+++ b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.css
@@ -131,7 +131,7 @@
}
.red {
background: rgba(188, 0, 10, 0.1);
- color: var(--color-danger);
+ color: rgba(188, 0, 10, 1);
}
.teal {
background: rgba(27, 155, 162, 0.1);
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
index d213c603..5a062773 100644
--- a/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.vue
+++ b/src/components/specific/projects/project-history-activity/activity-item/ActivityItem.vue
@@ -74,6 +74,28 @@
{{ $t("ProjectOverview.activity.newNameTitle") }}
{{ log.activity.details.newName }}
+
+
+
+ {{ $t("ProjectOverview.activity.oldPathTitle") }}
+
+
+
+
+ {{ $t("ProjectOverview.activity.newPathTitle") }}
+
+
+
diff --git a/src/config/activity-config.js b/src/config/activity-config.js
index befe937b..e151a85f 100644
--- a/src/config/activity-config.js
+++ b/src/config/activity-config.js
@@ -16,7 +16,7 @@ const PERMISSION_LIST = [
const getFileName = (path) => path?.split("/").pop() || "";
-const ACTION_CONFIG = {
+export const ACTION_CONFIG = {
document_created: {
actionKey: "ProjectOverview.activity.document_created",
getTarget: (log) => getFileName(log.description?.path),
@@ -170,6 +170,14 @@ export const getActivityFromLog = (log) => {
const roleKey = roleList[log.description?.project_role];
+ const getParentPath = (path) => path?.split("/").slice(0, -1).join("/") || "";
+
+ const oldPath = log.description?.old_path;
+ const newPath = log.description?.new_path;
+
+ const isMoved = oldPath && newPath && getParentPath(oldPath) !== getParentPath(newPath);
+ const isRenamed = oldPath && newPath && getFileName(oldPath) !== getFileName(newPath);
+
const newPermissionEntry = PERMISSION_LIST.find(
(entry) => entry.value === log.description?.new_permission,
);
@@ -195,10 +203,10 @@ export const getActivityFromLog = (log) => {
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,
+ oldName: !isMoved && isRenamed ? getFileName(oldPath) : null,
+ newName: !isMoved && isRenamed ? getFileName(newPath) : null,
+ oldPath: isMoved ? oldPath : null,
+ newPath: isMoved ? newPath : null,
},
};
};
diff --git a/src/i18n/lang/en.json b/src/i18n/lang/en.json
index 6c200ff1..9635601d 100644
--- a/src/i18n/lang/en.json
+++ b/src/i18n/lang/en.json
@@ -31,6 +31,8 @@
"empty": "No recent activity",
"title": "Activity history",
"folderTitle": "Folder:",
+ "newPathTitle": "New path:",
+ "oldPathTitle": "Old path:",
"roleTitle": "Role:",
"newPermissionTitle": "New rights:",
"oldPermissionTitle": "Old rights:",
@@ -40,22 +42,25 @@
"document_deleted": "File deleted:",
"document_renamed": "File renamed:",
"document_moved": "File moved:",
-
"folder_created": "Folder created:",
"folder_deleted": "Folder deleted:",
"folder_permissions_updated": "Folder permissions updated:",
"folder_renamed": "Folder renamed:",
"folder_moved": "Folder moved:",
-
"cloud_invitation_sent": "Invitation sent to:",
"cloud_invitation_canceled": "Invitation canceled for:",
"cloud_invitation_accepted": "Invitation accepted by:",
"cloud_invitation_denied": "Invitation denied by:",
-
"project_invitation_sent": "Project invitation sent to:",
"project_invitation_canceled": "Project invitation canceled for:",
"project_invitation_accepted": "Project invitation accepted by:",
"project_invitation_denied": "Project invitation denied by:",
+ "filters": {
+ "title": "Filters",
+ "types": "Types",
+ "users": "Users",
+ "period": "Period"
+ },
"roles": {
"admin": "Administrator",
"user": "User",
@@ -954,6 +959,7 @@
"t": {
"add": "Add",
"amount": "Amount",
+ "apply": "Apply",
"archive": "Archive",
"back": "Back",
"cancel": "Cancel",
@@ -972,6 +978,7 @@
"error": "Error",
"export": "Export",
"file": "File",
+ "filters": "Filters",
"folder": "Folder",
"import": "Import",
"invalidName": "Invalid name",
@@ -983,6 +990,7 @@
"open": "Open",
"or": "or",
"rename": "Rename",
+ "reset": "Reset",
"search": "Search",
"size": "Size",
"space": "Space",
diff --git a/src/i18n/lang/fr.json b/src/i18n/lang/fr.json
index 84d8fc31..6804246b 100644
--- a/src/i18n/lang/fr.json
+++ b/src/i18n/lang/fr.json
@@ -2,6 +2,7 @@
"t": {
"add": "Ajouter",
"amount": "Montant",
+ "apply": "Appliquer",
"archive": "Archiver",
"back": "Retour",
"cancel": "Annuler",
@@ -25,6 +26,7 @@
"export": "Exporter en BCF",
"exportXlsx": "Exporter en Excel",
"file": "Fichier",
+ "filters": "Filtres",
"folder": "Dossier",
"hours_ago": "il y a {count} heure | il y a {count} heures",
"import": "Importer",
@@ -43,6 +45,7 @@
"project": "Projet",
"projects": "Projets",
"rename": "Renommer",
+ "reset": "Réinitialiser",
"rootFolder": "Dossier racine",
"save": "Sauvegarder",
"search": "Rechercher",
@@ -99,6 +102,8 @@
"empty": "Aucune activité récente",
"title": "Historique d’activité",
"folderTitle": "Dossier :",
+ "newPathTitle": "Nouveau chemin :",
+ "oldPathTitle": "Ancien chemin :",
"roleTitle": "Rôle :",
"newPermissionTitle": "Nouveaux droits :",
"oldPermissionTitle": "Anciens droits :",
@@ -108,22 +113,25 @@
"document_deleted": "Suppression du fichier :",
"document_renamed": "Renommage du fichier :",
"document_moved": "Déplacement du fichier :",
-
"folder_created": "Création du dossier :",
"folder_deleted": "Suppression du dossier :",
"folder_permissions_updated": "Modification des permissions du dossier :",
"folder_renamed": "Renommage du dossier :",
"folder_moved": "Déplacement du dossier :",
-
"cloud_invitation_sent": "Invitation envoyée à :",
"cloud_invitation_canceled": "Invitation annulée pour :",
"cloud_invitation_accepted": "Invitation acceptée par :",
"cloud_invitation_denied": "Invitation refusée par :",
-
"project_invitation_sent": "Invitation projet envoyée à :",
"project_invitation_canceled": "Invitation projet annulée pour :",
"project_invitation_accepted": "Invitation projet acceptée par :",
"project_invitation_denied": "Invitation projet refusée par :",
+ "filters": {
+ "title": "Filtres",
+ "types": "Types",
+ "users": "Utilisateurs",
+ "period": "Période"
+ },
"roles": {
"admin": "Administrateur",
"user": "Utilisateur",