diff --git a/src/components/specific/files/all-files-table/AllFilesTable.vue b/src/components/specific/files/all-files-table/AllFilesTable.vue index 91700f909..6d9b0e4bd 100644 --- a/src/components/specific/files/all-files-table/AllFilesTable.vue +++ b/src/components/specific/files/all-files-table/AllFilesTable.vue @@ -215,6 +215,7 @@ :file="file" :loading="loadingFileIds.includes(file.id)" @create-model="$emit('create-model', file)" + @create-photosphere="$emit('create-photosphere', file)" @delete="$emit('delete', file)" @download="$emit('download', file)" @file-clicked="$emit('file-clicked', file)" @@ -283,6 +284,7 @@ export default { }, emits: [ "create-model", + "create-photosphere", "delete", "download", "file-clicked", diff --git a/src/components/specific/files/files-manager/FilesManager.vue b/src/components/specific/files/files-manager/FilesManager.vue index 4811c221d..ff53c7673 100644 --- a/src/components/specific/files/files-manager/FilesManager.vue +++ b/src/components/specific/files/files-manager/FilesManager.vue @@ -76,6 +76,7 @@ :foldersToUpload="foldersToUpload" @back-parent-folder="backToParent" @create-model="createModelFromFile" + @create-photosphere="createModelFromFile($event, MODEL_TYPE.PHOTOSPHERE)" @delete="openFileDeleteModal([$event])" @download="downloadFiles([$event])" @dragover.prevent="() => {}" @@ -100,6 +101,7 @@ :loadingFileIds="loadingFileIds" :files="displayedFiles" @create-model="createModelFromFile" + @create-photosphere="createModelFromFile($event, MODEL_TYPE.PHOTOSPHERE)" @delete="openFileDeleteModal([$event])" @download="downloadFiles([$event])" @file-clicked="onFileSelected" @@ -191,6 +193,7 @@ import { useAppSidePanel } from "../../app/app-side-panel/app-side-panel.js"; import { useSession } from "../../../../composables/session.js"; import { useListFilter } from "../../../../composables/list-filter.js"; import { useStandardBreakpoints } from "../../../../composables/responsive.js"; +import { MODEL_TYPE } from "../../../../config/models.js"; import { VISA_STATUS } from "../../../../config/visa.js"; import FileService from "../../../../services/FileService.js"; import TagService from "../../../../services/TagService"; @@ -271,7 +274,7 @@ export default { moveFiles: move, downloadFiles: download, } = useFiles(); - const { createModel, deleteModels } = useModels(); + const { createModel, createPhotosphere, deleteModels } = useModels(); const { fetchToValidateVisas, fetchCreatedVisas } = useVisa(); @@ -391,10 +394,15 @@ export default { const loadingFileIds = ref([]); - const createModelFromFile = async (file) => { + const createModelFromFile = async (file, type) => { try { loadingFileIds.value.push(file.id); - const model = await createModel(props.project, file); + let model; + if (type === MODEL_TYPE.PHOTOSPHERE) { + model = await createPhotosphere(props.project, file); + } else { + model = await createModel(props.project, file); + } emit("model-created", model); pushNotification({ type: "success", @@ -758,6 +766,7 @@ export default { folderToManage, importFromOtherProjectsActions, loadingFileIds, + MODEL_TYPE, searchText, selectedFileTab, selection, diff --git a/src/components/specific/files/files-table/file-actions-cell/FileActionsCell.vue b/src/components/specific/files/files-table/file-actions-cell/FileActionsCell.vue index 9eeed83de..da83159b5 100644 --- a/src/components/specific/files/files-table/file-actions-cell/FileActionsCell.vue +++ b/src/components/specific/files/files-table/file-actions-cell/FileActionsCell.vue @@ -76,6 +76,7 @@ export default { }, emits: [ "create-model", + "create-photosphere", "delete", "download", "file-clicked", @@ -139,6 +140,18 @@ export default { } } + if (!isFolder(props.file) && isConvertibleToPhotosphere(props.file)) { + if (!isModel(props.file)) { + menuItems.value.push({ + key: 3, + iconComponent: SetAsModelIcon, + text: "FileActionsCell.createPhotosphereButtonText", + disabled: !hasAdminPerm(props.project, props.file), + action: () => onClick("create-photosphere") + }); + } + } + menuItems.value.push({ key: 5, icon: "edit", diff --git a/src/components/specific/files/folder-table/FoldersTable.vue b/src/components/specific/files/folder-table/FoldersTable.vue index 897b9b4fa..19cc8f3b9 100644 --- a/src/components/specific/files/folder-table/FoldersTable.vue +++ b/src/components/specific/files/folder-table/FoldersTable.vue @@ -91,6 +91,7 @@ :file="file" :loading="loadingFileIds.includes(file.id)" @create-model="$emit('create-model', file)" + @create-photosphere="$emit('create-photosphere', file)" @delete="$emit('delete', file)" @download="$emit('download', file)" @file-clicked="$emit('file-clicked', file)" @@ -165,6 +166,7 @@ export default { emits: [ "back-parent-folder", "create-model", + "create-photosphere", "delete", "download", "file-clicked", diff --git a/src/components/specific/models/models-manager/ModelsManager.vue b/src/components/specific/models/models-manager/ModelsManager.vue index 3aadf4516..913e9d0d4 100644 --- a/src/components/specific/models/models-manager/ModelsManager.vue +++ b/src/components/specific/models/models-manager/ModelsManager.vue @@ -86,7 +86,7 @@ import IFCManager from "./ifc-manager/IFCManager.vue"; import PDFManager from "./pdf-manager/PDFManager.vue"; import PointCloudManager from "./point-cloud-manager/PointCloudManager.vue"; -const { DWG, DXF, IFC, JPEG, META_BUILDING, PDF, PHOTOSPHERE, PNG, POINT_CLOUD } = +const { DWG, DXF, IFC, META_BUILDING, PDF, PHOTOSPHERE, PHOTOSPHERE_BUILDING, POINT_CLOUD } = MODEL_TYPE; const tabsDef = [ @@ -132,7 +132,7 @@ const tabsDef = [ id: "photos", text: "Photos", icon: "fileImagePolychrome", - modelTypes: [JPEG, PHOTOSPHERE, PNG], + modelTypes: [PHOTOSPHERE, PHOTOSPHERE_BUILDING], component: "DWGManager" } ]; diff --git a/src/config/models.js b/src/config/models.js index 662f47550..379d162dd 100644 --- a/src/config/models.js +++ b/src/config/models.js @@ -17,6 +17,7 @@ const MODEL_TYPE = Object.freeze({ OBJ: "OBJ", PDF: "PDF", PHOTOSPHERE: "PHOTOSPHERE", + PHOTOSPHERE_BUILDING: "PHOTOSPHERE_BUILDING", PNG: "PNG", POINT_CLOUD: "POINT_CLOUD", }); @@ -121,6 +122,11 @@ const MODEL_CONFIG = Object.freeze({ window: WINDOWS.PHOTOSPHERE, icon: "fileImagePolychrome", }, + [MODEL_TYPE.PHOTOSPHERE_BUILDING]: { + ext: [], + window: WINDOWS.PHOTOSPHERE, + icon: "fileImagePolychrome", + }, [MODEL_TYPE.PNG]: { ext: [MODEL_EXTENSIONS.PNG], window: WINDOWS.PLAN, diff --git a/src/i18n/lang/fr.json b/src/i18n/lang/fr.json index b05f463ed..9d71aed6b 100644 --- a/src/i18n/lang/fr.json +++ b/src/i18n/lang/fr.json @@ -116,6 +116,7 @@ "addTagsButtonText": "Ajouter des tags", "addVersionButtonText": "Ajouter une version", "createModelButtonText": "Définir comme modèle", + "createPhotosphereButtonText": "Définir comme photosphère", "manageAccessButtonText": "Gérer les accès", "openViewerButtonText": "Ouvrir", "previewModelButtonText": "Prévisualiser", @@ -134,6 +135,7 @@ "FilesManager": { "title": "Documents du projet", "createModelNotification": "Modèle créé avec succès", + "createPhotosphereNotification": "Photosphère créée avec succès", "structureImport": "Importer une structure GED", "gedDownload": "Télécharger la GED", "folderImport": "Importer un dossier" diff --git a/src/services/ModelService.js b/src/services/ModelService.js index dd3bb0453..d34da329e 100644 --- a/src/services/ModelService.js +++ b/src/services/ModelService.js @@ -46,6 +46,25 @@ class ModelService { } } + async createPhotosphere(project, file) { + try { + return await fetch( + `${ENV.VUE_APP_API_BASE_URL}/cloud/${project.cloud.id}/project/${project.id}/model/create-photosphere`, + { + method: "POST", + credentials: "include", + headers: { + ...apiClient.authHeader, + "Content-Type": "application/json;charset=UTF-8", + }, + body: JSON.stringify({ document_id: file.id }) + } + ).then(res => res.json()); + } catch (error) { + throw new RuntimeError(ERRORS.MODEL_CREATE_ERROR, error); + } + } + async updateModels(project, models) { try { return await Promise.all( diff --git a/src/state/models.js b/src/state/models.js index 0d1be0f75..b8c5886f1 100644 --- a/src/state/models.js +++ b/src/state/models.js @@ -31,6 +31,11 @@ const createModel = async (project, file) => { return newModel; }; +const createPhotosphere = async (project, file) => { + const newModel = await ModelService.createPhotosphere(project, file); + return newModel; +}; + const updateModels = async (project, models) => { const newModels = await ModelService.updateModels(project, models); await loadProjectModels(project); @@ -171,6 +176,7 @@ export function useModels() { loadProjectModels, fetchModelByID, createModel, + createPhotosphere, updateModels, updateModelName, mergeModels, diff --git a/src/utils/models.js b/src/utils/models.js index 4b985122f..492d6e9f0 100644 --- a/src/utils/models.js +++ b/src/utils/models.js @@ -78,6 +78,13 @@ function isConvertible(document) { ); } +function isConvertibleToPhotosphere(document) { + const { JPEG, JPG } = MODEL_EXTENSIONS; + return [JPEG, JPG].includes( + fileExtension(document.file_name).toLowerCase() + ); +} + function openInViewer(router, project, model) { router.push({ name: routeNames.modelViewer, @@ -94,6 +101,7 @@ function openInViewer(router, project, model) { export { isConvertible, + isConvertibleToPhotosphere, isIFC, isModel, isPDF,