Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6ffb9ea
add History Activity for projects
LrxGaelle May 7, 2026
549fa8a
add icons for ProjectOverview tabs
LrxGaelle May 7, 2026
8cbcd2f
add click to path
LrxGaelle May 7, 2026
f7afe35
delete unused style
LrxGaelle May 7, 2026
cb6b0c5
delete unused darktheme ds config
LrxGaelle May 7, 2026
88e0f8d
add translation
LrxGaelle May 7, 2026
49fc49e
fix detail-path height
LrxGaelle May 7, 2026
4fac4a5
fix ActivityItem style
LrxGaelle May 18, 2026
b2dacce
delete unnecessary async
LrxGaelle May 18, 2026
05e9459
Added styling to the main tabs of the project overview.
LrxGaelle May 18, 2026
94fa2b6
move activity-config into src/config folder
LrxGaelle May 18, 2026
2ec0fc6
change translation
LrxGaelle May 18, 2026
d62969c
add searchbar
LrxGaelle May 18, 2026
65ed007
delete empty title
LrxGaelle May 18, 2026
5636011
add en translations
LrxGaelle May 18, 2026
8a8cd54
Add ProjectOverview title
LrxGaelle May 20, 2026
9678cca
remove folder_permissions_updated pending API improvements
LrxGaelle May 21, 2026
888ffd3
add bimdata textbox for activity target
LrxGaelle May 21, 2026
078d45e
add date
LrxGaelle May 21, 2026
af71c94
disable user-select for activity item title
LrxGaelle May 21, 2026
85e8975
display notifications & history for admin only
LrxGaelle May 22, 2026
ea77e86
add empty state for history activity
LrxGaelle May 22, 2026
38c133f
fix notifications panel height
LrxGaelle May 22, 2026
946e8bc
sync logs on project change and fix empty state check
LrxGaelle May 22, 2026
4c7c27b
fix go to GED view for history activity
LrxGaelle May 22, 2026
92f930e
fix detail style
LrxGaelle May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/components/specific/files/files-manager/FilesManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ export default {
if (selectedFileTab.value.id === "folders") {
// WARNING displayedRows is name from DS, may change
const folderFiles = filesTable.value.filesTable.displayedRows.map((row) => row.data);
documentList = folderFiles.filter(f => !isFolder(f));
documentList = folderFiles.filter((f) => !isFolder(f));
}
if (selectedFileTab.value.id === "files") {
documentList = filesTable.value.displayedListFiles;
Expand Down Expand Up @@ -663,11 +663,13 @@ export default {

onActivated(() => {
const folderId = gedTargetFolder.get();

if (folderId) {
jumpToTargetFolder(folderId);
jumpToTargetFolder(folderId, true);
} else {
jumpToTargetFolder(props.fileStructure.id);
}

gedTargetFolder.clear();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<ModelTagsCell :model="model" :parent="modelsTable" />
</template>
<template #cell-location="{ row: model }">
<div class="visas-table__location">
<div class="models-table__location">
<ModelPathCell
:model="model"
:allFolders="allFolders"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
maxWidth="200px"
/>
</div>

<transition name="fade">
<div v-if="hovering || showFullPath" class="model-path-cell__location">
<div
Expand Down Expand Up @@ -57,21 +57,15 @@
<BIMDataIconChevron size="xxxs" fill color="default" />
</div>
</div>

<span
v-else
class="folder-name flex items-center"
@click.stop="goToGedRoot"
>

<span v-else class="folder-name flex items-center" @click.stop="goToGedRoot">
<BIMDataIconFolderLocation fill color="primary" margin="0 6px 0 0" />
{{ $t("t.rootFolder") }}
</span>
</div>
</transition>
</template>
<template v-else>
-
</template>
<template v-else> - </template>
</div>
</template>

Expand Down Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.history-activity-panel {
height: calc(100vh - 44px - 40px - 12px - 12px);
overflow: auto;
.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;
}
}
.day-group {
margin-top: 20px;
.day-title {
font-size: 13px;
color: var(--color-granite);
margin-bottom: 8px;
font-weight: 600;
}

}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<template>
<div class="history-activity-panel">
<div class="header">
<h3>{{ $t("ProjectOverview.activity.title") }}</h3>
</div>

<BIMDataSearch
class="input-search"
width="95%"
:placeholder="$t('t.search')"
v-model="searchText"
clear
color="primary"
/>

<template v-if="hasDisplayedLogs">
<div v-for="(logs, day) in displayedGroupedLogs" :key="day" class="day-group">
<div class="day-title">{{ day }}</div>
<ActivityItem
v-for="log in logs"
:key="log.id"
:log="log"
:formatTimeAgo="formatTimeAgo"
@go-folder="$emit('go-folder', $event)"
/>
</div>
</template>
<template v-else>
<p>{{ $t("ProjectOverview.activity.empty") }}</p>
</template>
</div>
</template>

<script>
import { ref, onMounted, computed, watch } from "vue";
import { useI18n } from "vue-i18n";

import { getActivityFromLog } from "../../../../config/activity-config.js";
import { useTimeAgo } from "../../../../composables/time.js";
import ProjectService from "../../../../services/ProjectService.js";

import ActivityItem from "./activity-item/ActivityItem.vue";

export default {
props: {
project: Object,
},
components: {
ActivityItem,
},
emits: ["go-folder"],

setup(props) {
const { t, locale } = useI18n();

const logs = ref([]);
const searchText = ref("");

const { formatTimeAgo } = useTimeAgo();

const fetchLogs = async () => {
logs.value = [];

const fetchedLogs = await ProjectService.fetchLogs(props.project);

logs.value = fetchedLogs
.map((log) => {
const activity = getActivityFromLog(log);

if (!activity) return null;

return {
...log,
activity,
dateObj: new Date(log.date),

_search: [
log.action,
log.user_email,
log.project_name,
activity?.target,
log.path,
t(activity?.actionKey ?? ""),
]
.filter(Boolean)
.join(" ")
.toLowerCase(),
};
})
.filter(Boolean);
};

onMounted(fetchLogs);

watch(() => props.project, fetchLogs);

const formatDay = (date) => {
const today = new Date();
const yesterday = new Date();

yesterday.setDate(today.getDate() - 1);

if (date.toDateString() === today.toDateString()) {
return t("t.today");
}

if (date.toDateString() === yesterday.toDateString()) {
return t("t.yesterday");
}

return date.toLocaleDateString(locale.value, {
day: "numeric",
month: "long",
year: "numeric",
});
};

const sortedLogs = computed(() => logs.value.slice().sort((a, b) => b.dateObj - a.dateObj));

const displayedGroupedLogs = computed(() => {
const search = searchText.value.trim().toLowerCase();

return sortedLogs.value
.filter((log) => !search || log._search.includes(search))
.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);

return {
displayedGroupedLogs,
hasDisplayedLogs,
searchText,
formatTimeAgo,
};
},
};
</script>

<style scoped src="./ProjectHistoryActivity.css"></style>
Loading
Loading