Skip to content

Feature/model ged#625

Open
LrxGaelle wants to merge 7 commits intodevelopfrom
feature/model-ged
Open

Feature/model ged#625
LrxGaelle wants to merge 7 commits intodevelopfrom
feature/model-ged

Conversation

@LrxGaelle
Copy link
Member

@LrxGaelle LrxGaelle commented Feb 25, 2026

https://platform-dev-model-ged.bimdata.io

Description

This PR introduces two improvements in the GED:

  1. Replace type sorting with type filtering
  2. Add create models, create photospheres, and remove models actions based on multi-selection

Types of changes

  • Bug fix (non-breaking change which fixes an issue) DON'T FORGET TO SEND THE PR INTO RELEASE
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • My code follows the code style of this project.
  • I have tested my code.
  • My code add or change i18n tokens
  • I want to run the tests for the commits of this PR

@LrxGaelle LrxGaelle self-assigned this Feb 25, 2026
@LrxGaelle LrxGaelle changed the title [WIP] Feature/model ged Feature/model ged Feb 27, 2026
@LrxGaelle LrxGaelle removed the request for review from NicolasRichel February 27, 2026 12:59
@LrxGaelle LrxGaelle changed the title Feature/model ged [WIP] Feature/model ged Feb 27, 2026
@LrxGaelle LrxGaelle changed the title [WIP] Feature/model ged Feature/model ged Feb 27, 2026
if (!files?.length) return;
try {
selection.value = selection.value.map((f) => {
if (files.includes(f)) return { ...f, nature: "Model" };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to rebuild a selection ?
Maybe we can just change f.nature = "Model" and Vue reactivity works

loadingFileIds.value = loadingFileIds.value.filter((id) => id !== file.id);
}
};
const createModelFromFiles = async (files, type) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename files so we can know what are these files

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the GED files UI to replace “type” sorting with “type” filtering, and adds multi-selection actions to create models/photospheres or remove models.

Changes:

  • Replace “type” column sorting with filtering in folder and all-files tables, and compute a displayable type value for filtering.
  • Add multi-selection actions in the files action bar and implement bulk create-model/create-photosphere/remove-model flows in FilesManager.
  • Add i18n tokens for plural notifications and model/photosphere labels, and display model/photosphere info in the filename cell.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/i18n/lang/fr.json Adds new FileNameCell labels and plural/bulk notifications in French.
src/i18n/lang/en.json Adds new FileNameCell labels and plural/bulk notifications in English.
src/components/specific/files/folder-table/columns.js Switches “type” column from sortable to filterable.
src/components/specific/files/folder-table/FoldersTable.vue Feeds formatted rows (with computed type) to enable “type” filtering.
src/components/specific/files/files-table/file-name-cell/FileNameCell.vue Adds model/photosphere indicator tooltip/icon and related logic.
src/components/specific/files/files-manager/files-action-bar/FilesActionBar.vue Adds bulk actions (create models/photospheres, remove models) for multi-selection.
src/components/specific/files/files-manager/FilesManager.vue Implements bulk create/remove handlers and loading state propagation.
src/components/specific/files/all-files-table/columns.js Switches “type” column to filterable (removes sort function).
src/components/specific/files/all-files-table/AllFilesTable.vue Adds type filter UI, improves selection handling, and formats rows to include a computed type.
src/components/specific/files/all-files-table/AllFilesTable.scss Adds missing positioning styling for the “type” header/filter container.
Comments suppressed due to low confidence (3)

src/components/specific/files/all-files-table/AllFilesTable.vue:330

  • When fileExtension(file.name) returns an empty string (no extension), the current logic produces type: "" (because optional chaining still calls replace on ""). Add an explicit fallback so extensionless documents show t("t.file") (and folders show t("t.folder")).
            : file.name
              ? fileExtension(file.name)?.replace(".", "").toUpperCase()
              : t("t.file"),

src/components/specific/files/all-files-table/AllFilesTable.vue:371

  • The header checkbox uses a length equality check against displayedListFiles. With filters, selection may contain items not currently displayed, which forces an indeterminate state even if all displayed rows are selected. Compute this by checking whether every displayedListFiles item is selected (by id) instead of comparing lengths.
    const mainSelectionCheckboxValue = computed(() => {
      if (props.selection.length === 0) {
        return false;
      } else if (props.selection.length === displayedListFiles.value.length) {
        return true;

src/components/specific/files/files-manager/files-action-bar/FilesActionBar.vue:96

  • isModel is imported but never used in this component. Removing the unused import will avoid lint/build warnings and keep the module dependencies accurate.
import { computed } from "vue";
import { useToggle } from "../../../../../composables/toggle.js";
import { useUser } from "../../../../../state/user.js";
import { isFolder } from "../../../../../utils/file-structure.js";
import { isConvertible, isConvertibleToPhotosphere, isModel } from "../../../../../utils/models.js";

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +151 to +159
const canConvertAllToModel = computed(() =>
props.files.every(
(file) => !isFolder(file) && isConvertible(file) && file.nature === "Document",
),
);
const canConvertAllToPhotosphere = computed(() =>
props.files.every(
(file) => !isFolder(file) && isConvertibleToPhotosphere(file) && file.nature === "Document",
),
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canConvertAllToModel / canConvertAllToPhotosphere are based on props.files.every(...), which evaluates to true when props.files is empty. This will show the create-model/photosphere actions even when nothing is selected. Add a props.files.length > 0 guard (similar to canRemoveAllModels).

Suggested change
const canConvertAllToModel = computed(() =>
props.files.every(
(file) => !isFolder(file) && isConvertible(file) && file.nature === "Document",
),
);
const canConvertAllToPhotosphere = computed(() =>
props.files.every(
(file) => !isFolder(file) && isConvertibleToPhotosphere(file) && file.nature === "Document",
),
const canConvertAllToModel = computed(
() =>
props.files.length > 0 &&
props.files.every(
(file) => !isFolder(file) && isConvertible(file) && file.nature === "Document",
),
);
const canConvertAllToPhotosphere = computed(
() =>
props.files.length > 0 &&
props.files.every(
(file) =>
!isFolder(file) &&
isConvertibleToPhotosphere(file) &&
file.nature === "Document",
),

Copilot uses AI. Check for mistakes.
Comment on lines +379 to +384
selection.value = selection.value.map((f) => {
if (files.includes(f)) return { ...f, nature: "Model" };
return f;
});

loadingFileIds.value.push(...files.map((f) => f.id));
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createModelFromFiles optimistically sets selection items to nature: "Model" before the API calls and never rolls back on failure. If a conversion fails (or the request rejects), the UI can show items as models even though the backend state didn't change. Apply the UI update only after success, or revert it based on per-file results.

Copilot uses AI. Check for mistakes.
Comment on lines +438 to +439
if (!modelsToDelete.length) return;

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If modelsToDelete.length is 0, the function returns early after having already changed selection and added loadingFileIds. This leaves UI state modified even though no delete occurred. Consider checking modelsToDelete before mutating UI state, or ensure you revert selection/loading before returning.

Copilot uses AI. Check for mistakes.
position="right"
class="flex items-center"
>
<BIMDataIconSetAsModel v-if="file.nature === 'Model' || file.type === 'PHOTOSPHERE'" />
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The photosphere marker uses file.type === 'PHOTOSPHERE', but type is now used elsewhere as the displayed file extension ("PDF", etc.). Photosphere documents (model_type === 'PHOTOSPHERE' with nature === 'Document') won’t render the icon, making the tooltip effectively unreachable. Use file.model_type / MODEL_TYPE.PHOTOSPHERE (or isModel(file)) instead.

Suggested change
<BIMDataIconSetAsModel v-if="file.nature === 'Model' || file.type === 'PHOTOSPHERE'" />
<BIMDataIconSetAsModel v-if="file.nature === 'Model' || file.model_type === 'PHOTOSPHERE'" />

Copilot uses AI. Check for mistakes.
Comment on lines +379 to +382
selection.value = selection.value.map((f) => {
if (files.includes(f)) return { ...f, nature: "Model" };
return f;
});
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

files.includes(f) relies on object identity; selection items and the files arg may be different object instances with the same id (especially now that some tables map/format rows). Compare by id instead (e.g., build a Set of ids) to ensure the correct rows are updated.

Copilot uses AI. Check for mistakes.
Comment on lines +425 to +429
selection.value = selection.value.map((f) => {
if (files.includes(f)) return { ...f, nature: "Document" };
return f;
});
loadingFileIds.value.push(...files.map((f) => f.id));
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeModels optimistically mutates selection before deleting models and uses files.includes(f) (object identity). This can fail to update the intended rows and can also leave the UI in an incorrect state if deletion fails. Use id-based matching and update/revert selection based on actual deletion outcomes.

Copilot uses AI. Check for mistakes.
return props.allFiles.map((file) => ({
...file,
type:
file.nature === "folder"
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file.nature === "folder" doesn’t match the codebase’s folder nature ("Folder" / isFolder(file)). This will misclassify folders and produce incorrect type values for filtering/display. Use isFolder(file) or FILE_TYPE.FOLDER here.

Suggested change
file.nature === "folder"
file.nature === "Folder"

Copilot uses AI. Check for mistakes.
}
};

const removeModels = async (files) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if files === selection.value, we can just use selection.value

if (files.includes(f)) return { ...f, nature: "Document" };
return f;
});
loadingFileIds.value.push(...files.map((f) => f.id));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a boolean to disable the button instead of maintaining the list of ids ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants