Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
aab1fb1
add rule manager
Amoki Jun 26, 2026
2352931
assing constraint to folders
Amoki Jun 29, 2026
a5e0fd6
improve UX
Amoki Jun 29, 2026
6273ba3
improve rename modal
Amoki Jun 29, 2026
feb4c80
update lib & add modal when editing rule
Amoki Jul 1, 2026
06991b2
use css files instead of scss and separate style and template
LrxGaelle Jul 3, 2026
0a18efc
fix css import
LrxGaelle Jul 3, 2026
44b299b
add some UI fix
LrxGaelle Jul 6, 2026
32a84ff
add some fixs
LrxGaelle Jul 8, 2026
f1ba2b5
fix delete actions menu
LrxGaelle Jul 9, 2026
78c7dc3
fix delete constraint call
LrxGaelle Jul 9, 2026
f9c0119
refactor manager navigation with tabs
LrxGaelle Jul 9, 2026
44e915b
fixed footer action in constraint form
LrxGaelle Jul 9, 2026
1ed125b
fix empty templates list view
LrxGaelle Jul 9, 2026
014d7ed
preserve rule state when creating naming templates
LrxGaelle Jul 9, 2026
0d3a4f5
validate required fields before saving a rule
LrxGaelle Jul 10, 2026
bcbdb53
Refactor and reorganize the style
LrxGaelle Jul 10, 2026
3d93d58
start fix naming conflict modal
LrxGaelle Jul 16, 2026
82ad4c7
add modal for stric & non-stric rule & add folder info icon
LrxGaelle Jul 21, 2026
2e740e6
Add some styles for hamonize & refactor and replace duplicated previ…
LrxGaelle Jul 21, 2026
21df699
fix restore correct navigation after constraint and template creation
LrxGaelle Jul 24, 2026
c351c87
fix autofocus newly added template element
LrxGaelle Jul 27, 2026
69abd93
clean up template creation navigation state
LrxGaelle Jul 27, 2026
236e90e
fix hasNamingConflict for folder
LrxGaelle Jul 27, 2026
1156778
refresh GED after naming constraint updates
LrxGaelle Jul 27, 2026
163f899
add naming constraint editor for conflicting documents
LrxGaelle Jul 27, 2026
0576ac5
delete dead code
LrxGaelle Jul 28, 2026
93ee423
fix modal components style
LrxGaelle Jul 28, 2026
72366d6
fix overflow
LrxGaelle Jul 28, 2026
25e178a
fix acutal name in conflict modal
LrxGaelle Jul 28, 2026
24b2cbc
add translations
LrxGaelle Jul 28, 2026
08a9b0f
add safeZoneModal when cancel NamingConstraintsForm
LrxGaelle Jul 29, 2026
327cc40
refactor and move effective folder rule cache into NamingConstraintSe…
LrxGaelle Jul 29, 2026
a3f9ea7
add bulk import for naming template elements & add translations
LrxGaelle Jul 29, 2026
bbc16a4
delete unused translation key & add documents count on rename modal
LrxGaelle Jul 30, 2026
19a9fa3
add v-show on openNamingConstraintsManager to display only, on folder…
LrxGaelle Jul 30, 2026
f6c9463
display naming constraint preview for folders
LrxGaelle Aug 11, 2026
8fdc26e
fix folder rule selection and conflict notifications
LrxGaelle Aug 25, 2026
c264428
add current_key++ to menuItem
LrxGaelle Aug 25, 2026
4010c16
handle separators inside list values
LrxGaelle Aug 25, 2026
a1ea0da
fallback to global rule when document rule is missing
LrxGaelle Sep 24, 2026
4b7dab3
clean up naming conflict styles
LrxGaelle Sep 24, 2026
d33ff30
fix preserve rule draft when creating a template
LrxGaelle Oct 9, 2026
11fe073
fix show leave confirmation only for unsaved changes
LrxGaelle Oct 9, 2026
e015095
fix draft initialization and unsaved changes tracking & add onboardin…
LrxGaelle Oct 9, 2026
73be567
fix support template navigation in folder rule manager
LrxGaelle Oct 12, 2026
3fea88c
open naming conflict modal from file name cell
LrxGaelle Oct 12, 2026
4fb7dfd
fix truncate n_chars default value to max length
LrxGaelle Oct 12, 2026
d72a00f
inform users about inherited recursive rules & add counter on create …
LrxGaelle Oct 12, 2026
8639052
use new API for lists, block used list deletion, simplify constraint …
Amoki Oct 19, 2026
8373648
fix enforce strict rule validation in conflict modal
LrxGaelle Oct 20, 2026
16665ef
allow editing part names
LrxGaelle Oct 21, 2026
0243765
add modal for lists in use
LrxGaelle Oct 21, 2026
6ba87ac
fix add naming conflict modal to FileTypeCell and filter conflicting …
LrxGaelle Oct 22, 2026
319ec28
fix add default placeholder for rule part names
LrxGaelle Oct 23, 2026
3a62451
preserve default part names when override is empty & fix prevent fals…
LrxGaelle Oct 23, 2026
988194f
change recursiveLabel text
LrxGaelle Oct 26, 2026
02e2f88
add no list placeholder into RuleBuilder
LrxGaelle Oct 26, 2026
1cd2d3d
fix all folders for file path
LrxGaelle Oct 26, 2026
29fff09
fix BIMDataMenu in FileActionsCell
LrxGaelle Oct 26, 2026
b522e7a
fix FileNameCell preview click
LrxGaelle Oct 26, 2026
c3bd071
fix file-cliked
LrxGaelle Oct 27, 2026
9598986
fix clear false naming conflicts during file structure loading
LrxGaelle Oct 27, 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
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@bimdata/bcf-components": "6.7.8",
"@bimdata/components": "1.10.3",
"@bimdata/design-system": "2.4.4",
"@bimdata/typescript-fetch-api-client": "10.48.0",
"@bimdata/typescript-fetch-api-client": "10.48.1",
"@bimdata/viewer": "2.20.2-beta.7",
"@paddle/paddle-js": "^1.6.5",
"async": "^3.2.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@
@download="$emit('download', file)"
@file-clicked="$emit('file-clicked', file)"
@manage-access="$emit('manage-access', file)"
@manage-naming-rule="$emit('manage-naming-rule', file)"
@open-tag-manager="$emit('open-tag-manager', file)"
@open-versioning-manager="$emit('open-versioning-manager', file)"
@open-visa-manager="$emit('open-visa-manager', file)"
Expand Down Expand Up @@ -289,6 +290,7 @@ export default {
"file-clicked",
"go-folders-view",
"manage-access",
"manage-naming-rule",
"open-tag-manager",
"open-versioning-manager",
"open-visa-manager",
Expand Down
144 changes: 133 additions & 11 deletions src/components/specific/files/files-manager/FilesManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
:spaceSubInfo="spaceSubInfo"
:importFromOtherProjectsActions="importFromOtherProjectsActions"
:initialSearchText="searchText"
:refreshFiles="refreshFiles"
@update:searchText="searchText = $event"
@upload-files="uploadFiles"
@manage-naming-conflicts="openNamingConflictsModal"
/>
<div class="files-manager__content">
<transition name="slide-fade-left">
Expand Down Expand Up @@ -74,8 +76,9 @@
:loadingFileIds="loadingFileIds"
:selection="selection"
:filesToUpload="filesToUpload"
:folder="currentFolder"
:folder="folderWithNamingPreview"
:foldersToUpload="foldersToUpload"
:hasNamingConflict="hasNamingConflict"
@back-parent-folder="backToParent"
@create-model="createModelFromFile"
@create-photosphere="createModelFromFile($event, MODEL_TYPE.PHOTOSPHERE)"
Expand All @@ -88,6 +91,7 @@
@file-clicked="onFileSelected"
@file-uploaded="$emit('file-uploaded')"
@manage-access="openAccessManager"
@manage-naming-rule="openFolderNamingConstraintManager"
@open-tag-manager="openTagManager"
@open-versioning-manager="openVersioningManager"
@open-visa-manager="openVisaManager"
Expand All @@ -111,6 +115,7 @@
@file-clicked="onFileSelected"
@go-folders-view="goFoldersView"
@manage-access="openAccessManager"
@manage-naming-rule="openFolderNamingConstraintManager"
@open-tag-manager="openTagManager"
@open-versioning-manager="openVersioningManager"
@open-visa-manager="openVisaManager"
Expand Down Expand Up @@ -205,12 +210,14 @@ import FileService from "../../../../services/FileService.js";
import TagService from "../../../../services/TagService";
import { useFiles } from "../../../../state/files.js";
import { useModels } from "../../../../state/models.js";
import { useNamingConstraints } from "../../../../state/naming-constraints.js";
import { useProjects } from "../../../../state/projects.js";
import { useSpaces } from "../../../../state/spaces.js";
import { useVisa } from "../../../../state/visa.js";
import { collectDescendants } from "../../../../utils/file-tree.js";
import { isFolder } from "../../../../utils/file-structure.js";
import { getFilesFromEvent } from "../../../../utils/files.js";
import { matchName } from "../../../../utils/naming-constraint.js";
import { isFullTotal } from "../../../../utils/spaces.js";
import { fileUploadInput } from "../../../../utils/upload.js";

Expand All @@ -225,6 +232,8 @@ import FilesManagerOnboarding from "./files-manager-onboarding/FilesManagerOnboa
import FileTree from "../file-tree/FileTree.vue";
import FileTreePreviewModal from "../file-tree-preview-modal/FileTreePreviewModal.vue";
import FolderAccessManager from "../folder-access-manager/FolderAccessManager.vue";
import FolderNamingConstraintManager from "../naming-constraint/FolderNamingConstraintManager.vue";
import NamingConflictModal from "../naming-constraint/NamingConflictModal.vue";
import FoldersTable from "../folder-table/FoldersTable.vue";
import SubscriptionModal from "../../subscriptions/subscription-modal/SubscriptionModal.vue";
import TagsMain from "../../tags/tags-main/TagsMain.vue";
Expand Down Expand Up @@ -264,6 +273,10 @@ export default {
type: Object,
required: true,
},
refreshFiles: {
type: Function,
required: true,
},
},
emits: ["file-uploaded", "file-updated", "model-created"],
setup(props, { emit }) {
Expand All @@ -280,8 +293,14 @@ export default {
const { createModel, createPhotosphere, deleteModels } = useModels();

const { fetchToValidateVisas, fetchCreatedVisas } = useVisa();

const { getEffectiveFolderRule } = useNamingConstraints();
const currentFolder = ref(null);
const currentFolderNamingRule = ref(null);
const folderWithNamingPreview = computed(() =>
currentFolder.value
? { ...currentFolder.value, namingConstraintPreview: currentFolderNamingRule.value }
: currentFolder.value,
);
const currentFiles = ref([]);
const toValidateVisas = ref([]);
const createdVisas = ref([]);
Expand Down Expand Up @@ -337,20 +356,54 @@ export default {

const filesToUpload = ref([]);
const foldersToUpload = ref([]);
const uploadFiles = async (event, folder = currentFolder.value) => {
const { files, folders } = await getFilesFromEvent(event);
const proceedUpload = async ({ files, folders }, folder) => {
files.forEach((file) => (file.folder = folder));

filesToUpload.value = files;
foldersToUpload.value = await Promise.all(
folders.map((f) => FileService.createFolderStructure(props.project, folder, f)),
);

setTimeout(() => {
filesToUpload.value = [];
foldersToUpload.value = [];
}, 10);
};
let folders = [];
const uploadFiles = async (event, folder = currentFolder.value) => {
const { files, folders } = await getFilesFromEvent(event);

const rule = await getEffectiveFolderRule(props.project, folder);
const invalidFiles = rule ? files.filter((file) => !matchName(file.name, rule)) : [];

if (invalidFiles.length > 0) {
openModal({
component: NamingConflictModal,
props: {
project: props.project,
documents: invalidFiles.map((file, i) => ({ id: `upload-${i}`, name: file.name })),
rule,
allFolders: allFolders.value,
persistChanges: false,
onClose: closeModal,
onConfirm: ({ renamed, deleted }) => {
const deletedIds = new Set(deleted.map((d) => d.id));
const renamedById = new Map(renamed.map((r) => [r.id, r.name]));
const finalFiles = invalidFiles
.map((file, i) => ({ file, id: `upload-${i}` }))
.filter(({ id }) => !deletedIds.has(id))
.map(({ file, id }) => {
const name = renamedById.get(id);
return name ? new File([file], name, { type: file.type }) : file;
});
const validFiles = files.filter((file) => matchName(file.name, rule));
proceedUpload({ files: [...validFiles, ...finalFiles], folders }, folder);
},
},
});
return;
}

proceedUpload({ files, folders }, folder);
};

const loadingFileIds = ref([]);
const isCreatingModels = ref(false);
Expand Down Expand Up @@ -544,6 +597,50 @@ export default {
}, 100);
};

const openFolderNamingConstraintManager = (folder) => {
openSidePanel("right", {
component: FolderNamingConstraintManager,
props: {
project: props.project,
folder,
refreshFiles: props.refreshFiles,
},
});
};

const openNamingConflictsModal = async () => {
const conflicting = allFiles.value.filter((file) => file.naming_constraint_conflict);
if (conflicting.length === 0) {
pushNotification({
type: "success",
title: t("NamingConstraint.noConflictsTitle"),
message: t("NamingConstraint.noConflictsMessage"),
});
return;
}
const documents = await Promise.all(
conflicting.map(async (file) => {
const folder = allFolders.value.find((f) => f.id === file.parent_id);
const effective = folder ? await getEffectiveFolderRule(props.project, folder) : null;
return { ...file, namingRule: effective };
}),
);
openModal({
component: NamingConflictModal,
props: {
project: props.project,
documents,
allFolders: allFolders.value,
rule: null,
showRulesByGroup: true,
onClose: closeModal,
onConfirm: async () => {
await props.refreshFiles();
},
},
});
};

const visasLoading = ref(false);
const openVisaManager = (file) => {
onTabChange(filesTabs[2]);
Expand Down Expand Up @@ -683,6 +780,19 @@ export default {
const allFiles = computed(() => getFilesInFolder(props.fileStructure));
const allFolders = computed(() => getFoldersInFolder(props.fileStructure));

const hasNamingConflict = (folder) => {
if (!folder?.children?.length) {
return false;
}

return folder.children.some((child) => {
if (isFolder(child)) {
return hasNamingConflict(child);
}
return child.naming_constraint_conflict;
});
};

const filesTabs = [
{
id: "folders",
Expand Down Expand Up @@ -758,11 +868,15 @@ export default {
watch(
() => props.fileStructure,
(struct) => {
const folderId = gedTargetFolder.get();
const currentFolderId = currentFolder.value?.id;

if (currentFolderId) {
const folder = handler.get({
nature: FILE_TYPE.FOLDER,
id: currentFolderId,
});

if (folderId) {
jumpToTargetFolder(folderId);
gedTargetFolder.clear();
currentFolder.value = folder ? handler.deserialize(folder) : struct;
} else {
currentFolder.value = struct;
}
Expand All @@ -772,11 +886,15 @@ export default {

watch(
() => currentFolder.value,
(folder) => {
async (folder) => {
if (!folder) return;

const childrenFolders = folder.children.filter(isFolder).sort(sortByName);
const childrenFiles = folder.children.filter((c) => !isFolder(c)).sort(sortByName);

currentFiles.value = childrenFolders.concat(childrenFiles);
gedTargetFolder.set(folder.id);
currentFolderNamingRule.value = await getEffectiveFolderRule(props.project, folder);
},
{ immediate: true },
);
Expand All @@ -788,6 +906,7 @@ export default {
allTags,
allVisas,
currentFolder,
folderWithNamingPreview,
currentSpace,
currentVisa,
displayedFiles,
Expand Down Expand Up @@ -829,10 +948,13 @@ export default {
fileUploadInput,
goFoldersView,
goVisasView,
hasNamingConflict,
isFullTotal,
moveFiles,
onFileSelected,
openAccessManager,
openFolderNamingConstraintManager,
openNamingConflictsModal,
openFileDeleteModal,
openVisaDeleteModal,
openSidePanel,
Expand Down
Loading