Saltar para o conteúdo

Utilizador:LBelo (WMB)/gpgp.js: diferenças entre revisões

Fonte: Wikiversidade
Conteúdo apagado Conteúdo adicionado
LBelo (WMB) (discussão | contribs)
Sem resumo de edição
LBelo (WMB) (discussão | contribs)
Sem resumo de edição
Etiqueta: Revertida
Linha 29: Linha 29:
return {
return {
showDialog: false,
showDialog: false,
showFillButton: true,
showFillButton: false,
disableCreateButton: false,
disableCreateButton: false,
currentTab: 'Sobre',
currentTab: 'Sobre',

Revisão das 21h44min de 1 de novembro de 2024

/**
 * Esse arquivo JavaScript é parte do Gerador de Perfis de Grupos de Pesquisa (GPGP) (https://pt.wikiversity.org/wiki/Gerador_de_Perfis_de_Grupos_de_Pesquisa).
 * 
 * Autor:
 * @author Wiki Movimento Brasil (WMB) (https://meta.wikimedia.org/wiki/Wiki_Movement_Brazil_User_Group)
 * 
 * Contribuidores:
 * @contributors Lucas Belo (https://en.wikiversity.org/wiki/User:LBelo_(WMB))
 * @contributors Éder Porto (https://en.wikiversity.org/wiki/User:EPorto_(WMB))
 * 
 * Os recursos ultilizados são:
 * MediaWiki Frontend API (https://doc.wikimedia.org/mediawiki-core/master/js/index.html)
 * Codex - Wikimedia Design System (https://doc.wikimedia.org/codex/latest/)
 */


/**
 * Section for the Vue application and insertion of Codex components
 */
const elementGpgp = document.getElementById('gpgp-modal');

mw.loader.using('@wikimedia/codex').then(function (require) {
	const Vue = require('vue');
	const Codex = require('@wikimedia/codex');
	const mountPoint = elementGpgp.appendChild(document.createElement('div'));

	Vue.createMwApp({
		data: function () {
			return {
				showDialog: false,
				showFillButton: false,
				disableCreateButton: false,
				currentTab: 'Sobre',
				tabsData: [{
					name: 'Sobre',
					label: 'Sobre'
				}, {
					name: 'Pesquisadores',
					label: 'Pesquisadores'
				}, {
					name: 'Área',
					label: 'Área'
				}, {
					name: 'Identidade visual',
					label: 'Identidade visual'
				}, {
					name: 'Contato',
					label: 'Contato'
				}, {
					name: 'Submeter',
					label: 'Submeter'
				}],
				menuItemsSiteLanguage: [
					{ label: 'Português brasileiro', value: 'Q750553' },
					{ label: 'Português', value: 'Q5146' },
					{ label: 'Inglês', value: 'Q1860'},
					{ label: 'Espanhol', value: 'Q1321'},
					{ label: 'Francês', value: 'Q150'},
					{ label: 'Alemão', value: 'Q188'},
					{ label: 'Jamponês', value: 'Q5287'}
				],
				selectionSiteLanguage: 'Q750553',
				Wikidata: {
					message: '',
					type: 'notice',
					url: '',
					label: '',
					qid: ''
				},
				Wikiversity: {
					message: '',
					type: 'notice',
					url: '',
					page: ''
				}
			};
		},
		template: `
<cdx-button action="progressive" weight="primary">Criar Grupo de Pesquisa</cdx-button>

<cdx-dialog v-model:open="showDialog" title="Criar Grupo de Pesquisa" close-button-label="Close"
	:default-action="defaultAction" @default="open = false">

	<cdx-tabs v-model:active="currentTab" :framed="framed">
		<cdx-tab v-for="( tab, index ) in tabsData" :key="index" :name="tab.name" :label="tab.label"
			:disabled="tab.disabled">

			<template v-if="tab.name === 'Sobre'">
				<br>
				<cdx-button action="progressive" weight="primary" @click="fillFormButton" v-if="showFillButton">Preencher Formulário</cdx-button>
				
				<cdx-field :status="statusName" :messages="messagesName">
					<template #label>Nome</template>
					<cdx-text-input v-model="inputName" @blur="warningName" aria-label="TextInput default demo"></cdx-text-input>
					<template #help-text>Nome do grupo de pesquisa</template>
				</cdx-field>

				<cdx-field :status="statusDescription" :messages="messagesDescription">
					<template #label>Descrição</template>
					<cdx-text-area v-model="inputDescription" aria-label="TextArea default demo"></cdx-text-area>
				</cdx-field>

				<cdx-field optionalFlag="(opcional)">
					<template #label>Sigla</template>
					<template #help-text>Siglas ou abreviações do nome do grupo de pesquisa</template>
					<cdx-chip-input v-model:input-chips="aliasChips" remove-button-label="Remove"></cdx-chip-input>
				</cdx-field>

				<cdx-field>
					<template #label>Data de início das atividades</template>
					<cdx-text-input v-model="inputDate" aria-label="TextInput date demo"
						input-type="date"></cdx-text-input>
				</cdx-field>
				
				<cdx-field>
					<template #label>Países de atuação</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionCountry" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata"
						@input="onInput" @load-more="onLoadMore" @update:selected="insertSelectedInChipInput('selectionCountry')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-chip-input v-model:input-chips="countriesChips" remove-button-label="Remove"></cdx-chip-input>
				<p>Se não localizar um item no Wikidata, insira-o no campo acima</p>
				
				<cdx-field>
					<template #label>Localização</template>
					<template #help-text>Localização do grupo de pesquisa</template>
					<cdx-lookup v-model:selected="selectionLocal" :menu-items="menuItems" :menu-config="menuConfig"
						placeholder="Pesquise no Wikidata" @input="onInput" @load-more="onLoadMore">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #label>Instituições</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionInstitutions" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata"
						@input="onInput" @load-more="onLoadMore" @update:selected="insertSelectedInChipInput('selectionInstitutions')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-chip-input v-model:input-chips="institutionsChips" remove-button-label="Remove"></cdx-chip-input>
				<p>Se não localizar um item no Wikidata, insira-o no campo acima</p>
				<br>
			</template>

			<template v-if="tab.name === 'Pesquisadores'">
				<br>
				<cdx-field>
					<template #label>Pesquisador(es) principal(is)</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionPrincipalResearcher" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata" @input="onInput" @load-more="onLoadMore"
						@update:selected="insertSelectedInChipInput('selectionPrincipalResearcher')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #help-text>
						Se não localizar um item no Wikidata, insira-o no campo acima
					</template>
					<cdx-chip-input v-model:input-chips="principalResearcherChips" remove-button-label="Remove">
					</cdx-chip-input>
				</cdx-field>
				
				<br>
				<h5>Integrantes do grupo de pesquisa</h5>
				
				<cdx-field>
					<template #label>Pesquisadores associados</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionResearchers" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata" @input="onInput" @load-more="onLoadMore" 
						@update:selected="insertSelectedInChipInput('selectionResearchers')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #help-text>
						Se não localizar um item no Wikidata, insira-o no campo acima
					</template>
					<cdx-chip-input v-model:input-chips="researchersChips" remove-button-label="Remove">
					</cdx-chip-input>
				</cdx-field>

				<cdx-field>
					<template #label>Juniores</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionJuniors" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata" @input="onInput" @load-more="onLoadMore" 
						@update:selected="insertSelectedInChipInput('selectionJuniors')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #help-text>
						Se não localizar um item no Wikidata, insira-o no campo acima
					</template>
					<cdx-chip-input v-model:input-chips="juniorsChips" remove-button-label="Remove">
					</cdx-chip-input>
				</cdx-field>
				<br>
			</template>

			<template v-if="tab.name === 'Área'">
				<br>
				<cdx-field>
					<template #label>Área do conhecimento</template>
					<template #description>Tema de pesquisa ou campo de trabalho</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionTheme" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata"
						@input="onInput" @load-more="onLoadMore" @update:selected="insertSelectedInChipInput('selectionTheme')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #help-text>
						Se não localizar um item no Wikidata, insira-o no campo acima
					</template>
					<cdx-chip-input v-model:input-chips="themeChips" remove-button-label="Remove">
					</cdx-chip-input>
				</cdx-field>
				
				<cdx-field>
					<template #label>Palavras-chave</template>
					<template #help-text>
						Ao selecionar um item ele será automáticamente inserido no campo abaixo
					</template>
					<cdx-lookup v-model:selected="selectionKeyWord" :menu-items="menuItems" :menu-config="menuConfig"
						:clearable="true" placeholder="Pesquise no Wikidata"
						@input="onInput" @load-more="onLoadMore" @update:selected="insertSelectedInChipInput('selectionKeyWord')">
						<template #no-results>Nenhum resultado encontrado.</template>
					</cdx-lookup>
				</cdx-field>
				
				<cdx-field>
					<template #help-text>
						Se não localizar um item no Wikidata, insira-o no campo acima
					</template>
					<cdx-chip-input v-model:input-chips="keyWordChips" remove-button-label="Remove">
					</cdx-chip-input>
				</cdx-field>
				<br>
			</template>
			
			<template v-if="tab.name === 'Identidade visual'">
				<br>
				<cdx-field optionalFlag="(opcional)">
					<template #label>Cor</template>
					<template #help-text>Cor para o layout da página</template>
					<input id="colorPicker" type="color" value="#3366cc">
				</cdx-field>
				
				<cdx-field optionalFlag="(opcional)">
					<template #label>Logo</template>
					<template #help-text>Logo ou imagem do grupo de pesquisa no Wikimedia Commons</template>
					<cdx-text-input v-model="inputLogo" placeholder="Nome da imagem.jpg" aria-label="TextInput default demo"></cdx-text-input>
				</cdx-field>
				<br>
			</template>

			<template v-if="tab.name === 'Contato'">
				<br>
				<cdx-field optionalFlag="(opcional)">
					<template #label>E-mail</template>
					<cdx-text-input v-model="inputEmail" aria-label="TextInput default demo"></cdx-text-input>
				</cdx-field>
				
				<cdx-field optionalFlag="(opcional)">
					<template #label>Site</template>
					<cdx-text-input v-model="inputSite" aria-label="TextInput default demo"></cdx-text-input>
				</cdx-field>
				
				<cdx-field optionalFlag="(opcional)">
					<template #label>Idioma do site</template>
					<cdx-select v-model:selected="selectionSiteLanguage" :menu-items="menuItemsSiteLanguage"></cdx-select>
				</cdx-field>
				<br>
			</template>
			
			<template v-if="tab.name === 'Submeter'">
				<br>
				<cdx-message :type="Wikiversity.type" v-if="Wikiversity.message">
					{{ Wikiversity.message }}
					<p v-if="Wikiversity.url"><a :href="Wikiversity.url">{{ Wikiversity.page }}</a></p>
				</cdx-message>
				<cdx-message :type="Wikidata.type" v-if="Wikidata.message">
					{{ Wikidata.message }}
					<p v-if="Wikidata.url"><a :href="Wikidata.url">{{ Wikidata.label }} ({{ Wikidata.qid }})</a></p>
				</cdx-message>
				<center>
					<br>
					<cdx-button action="progressive" weight="primary" @click="createPages" :disabled="disableCreateButton">Criar Grupo de Pesquisa</cdx-button>
				</center>
			</template>
		</cdx-tab>
	</cdx-tabs>
</cdx-dialog>
		`,
		methods: {
			openDialog() {
				this.showDialog = true;
			},
			createPages() {
				function wikiversity(payload) {
					createWikiversityPages(payload) // see /addin-gpgp.js
					.then((result) => {
						payload.Wikiversity.message = 'Foi criada a página do grupo de pesquisa na Wikiversidade:';
						payload.Wikiversity.type = 'success';
						payload.Wikiversity.url = 'https://pt.wikiversity.org/wiki/' +  payload.inputName;
						payload.Wikiversity.page = payload.inputName;
						wikidata(payload);
						})
					.catch((error) => {
						payload.Wikiversity.message = 'Ocorreu um erro na criação da página do grupo de pesquisa na Wikiversidade.';
						payload.Wikiversity.type = 'error';
						console.log({ type: 'error', message: error });
						});
				}
				
				function wikidata(payload) {
					createWikidataItem(payload) // see /addin-gpgp.js
					.then((result) => {
						let qid = result.entity.id;
						let label = result.entity.labels["pt-br"].value;
						payload.Wikidata.message = 'Foi criado o item do grupo de pesquisa no Wikidata:';
						payload.Wikidata.type = 'success';
						payload.Wikidata.url = 'https://www.wikidata.org/wiki/' + qid;
						payload.Wikidata.label = label;
						payload.Wikidata.qid = qid;
						})
					.catch((error) => {
						payload.Wikidata.message = 'Ocorreu um erro na criação do item no Wikidata.';
						payload.Wikidata.type = 'error';
						console.log({ type: 'error', message: error });
						});
				}
					
				let checkName = false;
				let checkDescription = false;
				
				let inputName = this.inputName;
				if(inputName == '' || inputName === undefined){
					this.statusName = "error";
					this.messagesName = { error: 'Este campo é obrigatório.' };
					this.currentTab = 'Sobre';
				} else {
					this.statusName = "default";
					checkName = true;
				}
				
				let inputDescription = this.inputDescription;
				if(inputDescription == '' || inputDescription === undefined){
					this.statusDescription = "error";
					this.messagesDescription = { error: 'Este campo é obrigatório.' };
					this.currentTab = 'Sobre';
				} else {
					this.statusDescription = "default";
					checkDescription = true;
				}
				
				if(inputName.trim().toLowerCase() == inputDescription.trim().toLowerCase()) {
					this.statusDescription = "error";
					this.messagesDescription = { error: 'A descrição não pode ser igual ao nome do grupo de pesquisa.' };
					this.currentTab = 'Sobre';
					checkDescription = false;
				}
				
				if(checkName == true && checkDescription == true) {
					this.disableCreateButton = true;
					this.Wikiversity.message = 'Aguarde a criação da página do grupo de pesquisa na Wikiversidade...';
					this.Wikidata.message = 'Aguarde a criação do item do grupo de pesquisa no Wikidata...';
					wikiversity(this);
				}
			},
			fillFormButton() {
				fillForm(this); // see user:LBelo (WMB)/fillForm.js
			}
		},
		setup() {
			// selection objects
			const selectionLocal = ref(null);
			const selectionTheme = ref(null);
			const selectionKeyWord = ref(null);
			const selectionCountry = ref(null);
			const selectionJuniors = ref(null);
			const selectionResearchers = ref(null);
			const selectionInstitutions = ref(null);
			const selectionPrincipalResearcher = ref(null);
			
			// chips objects
			const themeChips = ref([]);
			const aliasChips = ref([]);
			const keyWordChips = ref([]);
			const juniorsChips = ref([]);
			const countriesChips = ref([]);
			const researchersChips = ref([]);
			const institutionsChips = ref([]);
			const principalResearcherChips = ref([]);
			
			// Lookup Wikidata
			const currentSearchTerm = ref('');
			const menuItems = ref([]);
			const menuConfig = {
				visibleItemLimit: 6,
				boldLabel: true
			};
			
			const messagesName = ref(null);
			const statusName = ref('default');
			function warningName(){
				statusName.value = 'warning';
				messagesName.value = {warning: "Revise o nome do grupo de pesquisa e verifique se há erros ou caracteres indevidos. Esse nome será usado em várias páginas, o que dificultará alterações futuras."};
				return;
			}

			function ref(value) {
				return Vue.ref(value);
			}

			function insertSelectedInChipInput(field) {
				var fields = {
					'selectionTheme': {'selection': selectionTheme, 'chips': themeChips},
					'selectionKeyWord': {'selection': selectionKeyWord, 'chips': keyWordChips},
					'selectionCountry': {'selection': selectionCountry, 'chips': countriesChips},
					'selectionJuniors': {'selection': selectionJuniors, 'chips': juniorsChips},
					'selectionResearchers': {'selection': selectionResearchers, 'chips': researchersChips},
					'selectionInstitutions': {'selection': selectionInstitutions, 'chips': institutionsChips},
					'selectionPrincipalResearcher': {'selection': selectionPrincipalResearcher, 'chips': principalResearcherChips},
				};
				
				var selection = fields[field].selection;
				var chips = fields[field].chips;
				
				// selected value has a QID
				if (selection.value !== null) {

					// iterates over suggested items to find the selected item
					for (let item of menuItems.value) {
						if (item.value == selection.value) {

							// manipulates chipInput values
							var payload = item.label + " (" + item.value + ")";
							var payloadInChips = false;
							for (let chip of chips.value) {
								if (payload == chip.value) {
									payloadInChips = true;
									break;
								}
							}
							if (payloadInChips == false) {
								chips.value.push({ value: payload });
								return;
							}
						}
					}
				}
			}

			/**
			 * Get search results.
			 *
			 * @param {string} searchTerm
			 * @param {number} offset Optional result offset
			 *
			 * @return {Promise}
			 */
			function fetchResults(searchTerm, offset) {
				const params = new URLSearchParams({
					origin: '*',
					action: 'wbsearchentities',
					format: 'json',
					limit: '10',
					props: 'url',
					language: 'pt-br',
					uselang: 'pt-br',
					search: searchTerm
				});
				if (offset) {
					params.set('continue', String(offset));
				}
				return fetch(`https://www.wikidata.org/w/api.php?${params.toString()}`)
					.then((response) => response.json());
			}

			/**
			 * Handle lookup input.
			 *
			 * TODO: this should be debounced.
			 *
			 * @param {string} value
			 */
			function onInput(value) {
				// Internally track the current search term.
				currentSearchTerm.value = value;

				// Do nothing if we have no input.
				if (!value) {
					menuItems.value = [];
					return;
				}

				fetchResults(value)
					.then((data) => {
						// Make sure this data is still relevant first.
						if (currentSearchTerm.value !== value) {
							return;
						}

						// Reset the menu items if there are no results.
						if (!data.search || data.search.length === 0) {
							menuItems.value = [];
							return;
						}

						// Build an array of menu items.
						const results = data.search.map((result) => {
							return {
								label: result.label,
								value: result.id,
								description: result.description
							};
						});

						// Update menuItems.
						menuItems.value = results;
					})
					.catch(() => {
						// On error, set results to empty.
						menuItems.value = [];
					});
			}

			function deduplicateResults(results) {
				const seen = new Set(menuItems.value.map((result) => result.value));
				return results.filter((result) => !seen.has(result.value));
			}

			function onLoadMore() {
				if (!currentSearchTerm.value) {
					return;
				}

				fetchResults(currentSearchTerm.value, menuItems.value.length)
					.then((data) => {
						if (!data.search || data.search.length === 0) {
							return;
						}

						const results = data.search.map((result) => {
							return {
								label: result.label,
								value: result.id,
								description: result.description
							};
						});

						// Update menuItems.
						const deduplicatedResults = deduplicateResults(results);
						menuItems.value.push(...deduplicatedResults);
					});
			}

			return {
				// functions
				onInput,
				onLoadMore,
				warningName,
				insertSelectedInChipInput,
				
				// Messages
				messagesName,
				statusName,
				
				// lookup objects
				menuItems,
				menuConfig,
				
				// chips objects
				themeChips,
				aliasChips,
				keyWordChips,
				juniorsChips,
				countriesChips,
				researchersChips,
				institutionsChips,
				principalResearcherChips,
				
				// selection objects
				selectionLocal,
				selectionTheme,
				selectionKeyWord,
				selectionCountry,
				selectionJuniors,
				selectionResearchers,
				selectionInstitutions,
				selectionPrincipalResearcher,
			};
		},
		mounted() {
			elementGpgp.addEventListener('click', this.openDialog);
		},
		unMounted() {
			elementGpgp.removeEventListener(this.openDialog);
		}
	})
		.component('cdx-tab', Codex.CdxTab)
		.component('cdx-tabs', Codex.CdxTabs)
		.component('cdx-field', Codex.CdxField)
		.component('cdx-lookup', Codex.CdxLookup)
		.component('cdx-button', Codex.CdxButton)
		.component('cdx-dialog', Codex.CdxDialog)
		.component('cdx-select', Codex.CdxSelect)
		.component('cdx-message', Codex.CdxMessage)
		.component('cdx-text-area', Codex.CdxTextArea)
		.component('cdx-accordion', Codex.CdxAccordion)
		.component('cdx-text-input', Codex.CdxTextInput)
		.component('cdx-chip-input', Codex.CdxChipInput)
		.mount(mountPoint);
});