Skip to content

Commit 03d87b4

Browse files
author
Ryan Coulson
committed
add export product button
1 parent bc2f3c0 commit 03d87b4

File tree

3 files changed

+169
-87
lines changed

3 files changed

+169
-87
lines changed

src/components/editor.vue

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,50 @@
177177
>
178178
<span class="bottom-0 question-mark-button"> ? </span>
179179
</button>
180+
181+
<!-- Export button -->
182+
<button
183+
@click="exportProduct"
184+
class="bg-white border border-black rounded-full w-9 h-9 flex items-center justify-center hover:bg-gray-100"
185+
v-tippy="{
186+
delay: '200',
187+
placement: 'top',
188+
content: $t('editor.export'),
189+
animateFill: true
190+
}"
191+
:aria-label="$t('editor.export')"
192+
>
193+
<span class="bottom-0 question-mark-button">
194+
<svg
195+
width="20px"
196+
height="20px"
197+
viewBox="0 0 24 24"
198+
fill="none"
199+
xmlns="http://www.w3.org/2000/svg"
200+
stroke="#000000"
201+
stroke-width="0.336"
202+
>
203+
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
204+
<g
205+
id="SVGRepo_tracerCarrier"
206+
stroke-linecap="round"
207+
stroke-linejoin="round"
208+
stroke="#CCCCCC"
209+
stroke-width="0.288"
210+
></g>
211+
<g id="SVGRepo_iconCarrier">
212+
<path
213+
d="M12.5535 16.5061C12.4114 16.6615 12.2106 16.75 12 16.75C11.7894 16.75 11.5886 16.6615 11.4465 16.5061L7.44648 12.1311C7.16698 11.8254 7.18822 11.351 7.49392 11.0715C7.79963 10.792 8.27402 10.8132 8.55352 11.1189L11.25 14.0682V3C11.25 2.58579 11.5858 2.25 12 2.25C12.4142 2.25 12.75 2.58579 12.75 3V14.0682L15.4465 11.1189C15.726 10.8132 16.2004 10.792 16.5061 11.0715C16.8118 11.351 16.833 11.8254 16.5535 12.1311L12.5535 16.5061Z"
214+
fill="#000"
215+
></path>
216+
<path
217+
d="M3.75 15C3.75 14.5858 3.41422 14.25 3 14.25C2.58579 14.25 2.25 14.5858 2.25 15V15.0549C2.24998 16.4225 2.24996 17.5248 2.36652 18.3918C2.48754 19.2919 2.74643 20.0497 3.34835 20.6516C3.95027 21.2536 4.70814 21.5125 5.60825 21.6335C6.47522 21.75 7.57754 21.75 8.94513 21.75H15.0549C16.4225 21.75 17.5248 21.75 18.3918 21.6335C19.2919 21.5125 20.0497 21.2536 20.6517 20.6516C21.2536 20.0497 21.5125 19.2919 21.6335 18.3918C21.75 17.5248 21.75 16.4225 21.75 15.0549V15C21.75 14.5858 21.4142 14.25 21 14.25C20.5858 14.25 20.25 14.5858 20.25 15C20.25 16.4354 20.2484 17.4365 20.1469 18.1919C20.0482 18.9257 19.8678 19.3142 19.591 19.591C19.3142 19.8678 18.9257 20.0482 18.1919 20.1469C17.4365 20.2484 16.4354 20.25 15 20.25H9C7.56459 20.25 6.56347 20.2484 5.80812 20.1469C5.07435 20.0482 4.68577 19.8678 4.40901 19.591C4.13225 19.3142 3.9518 18.9257 3.85315 18.1919C3.75159 17.4365 3.75 16.4354 3.75 15Z"
218+
fill="#000"
219+
></path>
220+
</g>
221+
</svg>
222+
</span>
223+
</button>
180224
</div>
181225
</div>
182226
</div>
@@ -543,6 +587,14 @@ export default class EditorV extends Vue {
543587
});
544588
}
545589
590+
exportProduct(): void {
591+
if (this.$refs.slide != null && this.currentSlide !== '') {
592+
(this.$refs.slide as SlideEditorV).saveChanges();
593+
}
594+
595+
this.$emit('export-product');
596+
}
597+
546598
/**
547599
* Open current editor config as a new Storylines product in new tab.
548600
* @param language The config language to preview (either 'en' or 'fr')
@@ -687,8 +739,9 @@ window.addEventListener('resize', () => {
687739
}
688740
689741
.question-mark-button {
690-
font-size: 24px;
691-
line-height: 1.8rem;
742+
font-size: 20px;
743+
line-height: 1.4rem;
744+
font-weight: 500;
692745
}
693746
694747
.toc-popup-button {

src/components/metadata-editor.vue

Lines changed: 111 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@
514514
@save-changes="onSave"
515515
@save-status="updateSaveStatus"
516516
@refresh-config="refreshConfig"
517+
@export-product="exportProduct"
517518
ref="mainEditor"
518519
>
519520
<!-- Metadata editing modal inside the editor -->
@@ -591,6 +592,7 @@ import { useUserStore } from '../stores/userStore';
591592
import JSZip from 'jszip';
592593
import axios from 'axios';
593594
import { v4 as uuidv4 } from 'uuid';
595+
import { saveAs } from 'file-saver';
594596
595597
import Message from 'vue-m-message';
596598
import SlideEditorV from './slide-editor.vue';
@@ -1334,6 +1336,20 @@ export default class MetadataEditorV extends Vue {
13341336
this.temporaryMetadataCopy = JSON.parse(JSON.stringify(this.metadata));
13351337
}
13361338
1339+
exportProduct(): void {
1340+
this.generateConfig(false);
1341+
1342+
this.configFileStructure?.zip.generateAsync({ type: 'blob' }).then(
1343+
(blob) => {
1344+
saveAs(blob, `${this.configFileStructure?.uuid}.zip`);
1345+
Message.success(this.$t('editor.export.success'));
1346+
},
1347+
(err) => {
1348+
Message.error(this.$t('editor.export.error'));
1349+
}
1350+
);
1351+
}
1352+
13371353
/**
13381354
* Conducts various checks before saving.
13391355
*/
@@ -1345,9 +1361,9 @@ export default class MetadataEditorV extends Vue {
13451361
13461362
/**
13471363
* Called when `Save Changes` is pressed. Re-generates the Storylines configuration file
1348-
* with the new changes, then generates and submits the product file to the server.
1364+
* with the new changes, and if `publish` is set to true, generates and submits the product file to the server.
13491365
*/
1350-
generateConfig(): ConfigFileStructure {
1366+
generateConfig(publish = true): ConfigFileStructure {
13511367
this.saving = true;
13521368
13531369
// Update the configuration files, for both languages.
@@ -1370,97 +1386,107 @@ export default class MetadataEditorV extends Vue {
13701386
this.configFileStructure?.zip.file(frFileName, frFormattedConfigFile);
13711387
13721388
// Upload the ZIP file.
1373-
this.configFileStructure?.zip.generateAsync({ type: 'blob' }).then((content: Blob) => {
1374-
const formData = new FormData();
1375-
formData.append('data', content, `${this.uuid}.zip`);
1376-
const userStore = useUserStore();
1377-
const headers = { 'Content-Type': 'multipart/form-data', user: userStore.userProfile.userName || 'Guest' };
1378-
Message.warning(this.$t('editor.editMetadata.message.wait'));
1389+
if (publish) {
1390+
this.configFileStructure?.zip.generateAsync({ type: 'blob' }).then((content: Blob) => {
1391+
const formData = new FormData();
1392+
formData.append('data', content, `${this.uuid}.zip`);
1393+
const userStore = useUserStore();
1394+
const headers = {
1395+
'Content-Type': 'multipart/form-data',
1396+
user: userStore.userProfile.userName || 'Guest'
1397+
};
1398+
Message.warning(this.$t('editor.editMetadata.message.wait'));
1399+
1400+
axios
1401+
.post(this.apiUrl + '/upload', formData, { headers })
1402+
.then((res: AxiosResponse) => {
1403+
const responseData = res.data;
1404+
responseData.files; // binary representation of the file
1405+
responseData.status; // HTTP status
1406+
const commitHash = responseData.commitHash; // commit hash of the git commit
1407+
this.unsavedChanges = false;
1408+
this.loadExisting = true; // if editExisting was false, we can now set it to true
1409+
1410+
if (import.meta.env.VITE_APP_CURR_ENV) {
1411+
if (responseData.new) {
1412+
axios
1413+
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/user/register', {
1414+
uuid: this.uuid,
1415+
titleEn: this.configs['en']?.title ?? '',
1416+
titleFr: this.configs['fr']?.title ?? ''
1417+
})
1418+
.then((response: any) => {
1419+
const userStore = useUserStore();
1420+
userStore.fetchUserProfile();
1421+
formData.append('uuid', this.uuid);
1422+
formData.append('titleEn', this.configs['en']?.title ?? '');
1423+
formData.append('titleFr', this.configs['fr']?.title ?? '');
1424+
formData.append('commitHash', commitHash);
1425+
formData.delete('data'); // Remove the data from the form so that we don't pass it into the .NET API
1426+
axios
1427+
.post(
1428+
import.meta.env.VITE_APP_NET_API_URL + '/api/version/commit',
1429+
formData
1430+
)
1431+
.then((response: any) => {
1432+
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1433+
})
1434+
.catch((error: any) => console.log(error.response || error))
1435+
.finally(() => {
1436+
// padding to prevent save button from being clicked rapidly
1437+
setTimeout(() => {
1438+
this.saving = false;
1439+
}, 500);
1440+
});
1441+
})
1442+
.catch((error: any) => console.log(error.response || error));
1443+
} else {
1444+
formData.append('uuid', this.uuid);
1445+
formData.append('titleEn', this.configs['en']?.title ?? '');
1446+
formData.append('titleFr', this.configs['fr']?.title ?? '');
1447+
formData.append('commitHash', commitHash);
1448+
formData.delete('data'); // Remove the data from the form so that we don't pass it into the .NET API
1449+
axios
1450+
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/version/commit', formData)
1451+
.then((response: any) => {
1452+
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1453+
})
1454+
.catch((error: any) => console.log(error.response || error))
1455+
.finally(() => {
1456+
// padding to prevent save button from being clicked rapidly
1457+
setTimeout(() => {
1458+
this.saving = false;
1459+
}, 500);
1460+
});
1461+
}
13791462
1380-
axios
1381-
.post(this.apiUrl + '/upload', formData, { headers })
1382-
.then((res: AxiosResponse) => {
1383-
const responseData = res.data;
1384-
responseData.files; // binary representation of the file
1385-
responseData.status; // HTTP status
1386-
const commitHash = responseData.commitHash; // commit hash of the git commit
1387-
this.unsavedChanges = false;
1388-
this.loadExisting = true; // if editExisting was false, we can now set it to true
1389-
1390-
if (import.meta.env.VITE_APP_CURR_ENV) {
1391-
if (responseData.new) {
1392-
axios
1393-
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/user/register', {
1394-
uuid: this.uuid,
1395-
titleEn: this.configs['en']?.title ?? '',
1396-
titleFr: this.configs['fr']?.title ?? ''
1463+
fetch(this.apiUrl + `/retrieveMessages`)
1464+
.then((res: any) => {
1465+
if (res.ok) return res.json();
13971466
})
1398-
.then((response: any) => {
1399-
const userStore = useUserStore();
1400-
userStore.fetchUserProfile();
1401-
formData.append('uuid', this.uuid);
1402-
formData.append('titleEn', this.configs['en']?.title ?? '');
1403-
formData.append('titleFr', this.configs['fr']?.title ?? '');
1404-
formData.append('commitHash', commitHash);
1405-
formData.delete('data'); // Remove the data from the form so that we don't pass it into the .NET API
1467+
.then((data) => {
14061468
axios
1407-
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/version/commit', formData)
1408-
.then((response: any) => {
1409-
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1469+
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/log/create', {
1470+
messages: data.messages
14101471
})
1411-
.catch((error: any) => console.log(error.response || error))
1412-
.finally(() => {
1413-
// padding to prevent save button from being clicked rapidly
1414-
setTimeout(() => {
1415-
this.saving = false;
1416-
}, 500);
1417-
});
1472+
.catch((error: any) => console.log(error.response || error));
14181473
})
14191474
.catch((error: any) => console.log(error.response || error));
14201475
} else {
1421-
formData.append('uuid', this.uuid);
1422-
formData.append('titleEn', this.configs['en']?.title ?? '');
1423-
formData.append('titleFr', this.configs['fr']?.title ?? '');
1424-
formData.append('commitHash', commitHash);
1425-
formData.delete('data'); // Remove the data from the form so that we don't pass it into the .NET API
1426-
axios
1427-
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/version/commit', formData)
1428-
.then((response: any) => {
1429-
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1430-
})
1431-
.catch((error: any) => console.log(error.response || error))
1432-
.finally(() => {
1433-
// padding to prevent save button from being clicked rapidly
1434-
setTimeout(() => {
1435-
this.saving = false;
1436-
}, 500);
1437-
});
1476+
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1477+
// padding to prevent save button from being clicked rapidly
1478+
setTimeout(() => {
1479+
this.saving = false;
1480+
}, 500);
14381481
}
1439-
1440-
fetch(this.apiUrl + `/retrieveMessages`)
1441-
.then((res: any) => {
1442-
if (res.ok) return res.json();
1443-
})
1444-
.then((data) => {
1445-
axios
1446-
.post(import.meta.env.VITE_APP_NET_API_URL + '/api/log/create', {
1447-
messages: data.messages
1448-
})
1449-
.catch((error: any) => console.log(error.response || error));
1450-
})
1451-
.catch((error: any) => console.log(error.response || error));
1452-
} else {
1453-
Message.success(this.$t('editor.editMetadata.message.successfulSave'));
1454-
// padding to prevent save button from being clicked rapidly
1455-
setTimeout(() => {
1456-
this.saving = false;
1457-
}, 500);
1458-
}
1459-
})
1460-
.catch(() => {
1461-
Message.error(this.$t('editor.editMetadata.message.error.failedSave'));
1462-
});
1463-
});
1482+
})
1483+
.catch(() => {
1484+
Message.error(this.$t('editor.editMetadata.message.error.failedSave'));
1485+
});
1486+
});
1487+
} else {
1488+
this.saving = false;
1489+
}
14641490
14651491
return this.configFileStructure as ConfigFileStructure;
14661492
}

src/lang/lang.csv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ editor.contextLabel,Context label,1,Étiquette de contexte,1
110110
editor.contextLabel.info,Context label shows up before the context link to explain what the link is for,1,L’étiquette de contexte apparaît avant le lien contextuel et explique à quoi sert le lien.,1
111111
editor.dateModified,Date modified,1,Date de modification,1
112112
editor.load,Load,1,Charger,1
113+
editor.export,Export,1,Exporter,0
114+
editor.export.success,Export successful,1,Exportation réussie,0
115+
editor.export.error,Export failed,1,Échec de l’exportation,0
113116
editor.rename,Rename,1,Rename,0
114117
editor.loadPrevious,Load Previous,1,Charger le précédent,0
115118
editor.viewHistory,View Previous,1,Voir précédent,0

0 commit comments

Comments
 (0)