forked from geolba/tethys.backend
- added npm package dotenv-webpack for using env variables on clientside
- added API File Controller for downloading files e.g. /api/download/1022 - also create has codes by submitting new dataset - added edit dataset functionalities for role submitter - added the following route for role submitter: /dataset/:id/update', 'DatasetController.update' - created extra UpdateDatasetValidator.ts for validating updated dataset - npm updates
This commit is contained in:
parent
a7142f694f
commit
d8bdce1369
54
app/Controllers/Http/Api/FileController.ts
Normal file
54
app/Controllers/Http/Api/FileController.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
import File from 'App/Models/File';
|
||||||
|
import { StatusCodes } from 'http-status-codes';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
// node ace make:controller Author
|
||||||
|
export default class FileController {
|
||||||
|
// @Get("download/:id")
|
||||||
|
public async findOne({ response, params }: HttpContextContract) {
|
||||||
|
const id = params.id;
|
||||||
|
const file = await File.findOrFail(id);
|
||||||
|
// const file = await File.findOne({
|
||||||
|
// where: { id: id },
|
||||||
|
// });
|
||||||
|
if (file) {
|
||||||
|
const filePath = '/storage/app/public/' + file.pathName;
|
||||||
|
const ext = path.extname(filePath);
|
||||||
|
const fileName = file.label + ext;
|
||||||
|
try {
|
||||||
|
fs.accessSync(filePath, fs.constants.R_OK); //| fs.constants.W_OK);
|
||||||
|
// console.log("can read/write:", path);
|
||||||
|
|
||||||
|
response
|
||||||
|
.header('Cache-Control', 'no-cache private')
|
||||||
|
.header('Content-Description', 'File Transfer')
|
||||||
|
.header('Content-Type', file.mimeType)
|
||||||
|
.header('Content-Disposition', 'inline; filename=' + fileName)
|
||||||
|
.header('Content-Transfer-Encoding', 'binary')
|
||||||
|
.header('Access-Control-Allow-Origin', '*')
|
||||||
|
.header('Access-Control-Allow-Methods', 'GET,POST');
|
||||||
|
|
||||||
|
response.status(StatusCodes.OK).download(filePath);
|
||||||
|
} catch (err) {
|
||||||
|
// console.log("no access:", path);
|
||||||
|
response.status(StatusCodes.NOT_FOUND).send({
|
||||||
|
message: `File with id ${id} doesn't exist on file server`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// res.status(StatusCodes.OK).sendFile(filePath, (err) => {
|
||||||
|
// // res.setHeader("Content-Type", "application/json");
|
||||||
|
// // res.removeHeader("Content-Disposition");
|
||||||
|
// res.status(StatusCodes.NOT_FOUND).send({
|
||||||
|
// message: `File with id ${id} doesn't exist on file server`,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
} else {
|
||||||
|
response.status(StatusCodes.NOT_FOUND).send({
|
||||||
|
message: `Cannot find File with id=${id}.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import Database from '@ioc:Adonis/Lucid/Database';
|
||||||
import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
|
import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
|
||||||
import Subject from 'App/Models/Subject';
|
import Subject from 'App/Models/Subject';
|
||||||
import CreateDatasetValidator from 'App/Validators/CreateDatasetValidator';
|
import CreateDatasetValidator from 'App/Validators/CreateDatasetValidator';
|
||||||
|
import UpdateDatasetValidator from 'App/Validators/UpdateDatasetValidator';
|
||||||
import {
|
import {
|
||||||
TitleTypes,
|
TitleTypes,
|
||||||
DescriptionTypes,
|
DescriptionTypes,
|
||||||
|
@ -33,8 +34,6 @@ import ClamScan from 'clamscan';
|
||||||
import { ValidationException } from '@ioc:Adonis/Core/Validator';
|
import { ValidationException } from '@ioc:Adonis/Core/Validator';
|
||||||
import Drive from '@ioc:Adonis/Core/Drive';
|
import Drive from '@ioc:Adonis/Core/Drive';
|
||||||
import { Exception } from '@adonisjs/core/build/standalone';
|
import { Exception } from '@adonisjs/core/build/standalone';
|
||||||
// import XmlModel from 'App/Library/XmlModel';
|
|
||||||
// import { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
|
|
||||||
|
|
||||||
export default class DatasetController {
|
export default class DatasetController {
|
||||||
public async index({ auth, request, inertia }: HttpContextContract) {
|
public async index({ auth, request, inertia }: HttpContextContract) {
|
||||||
|
@ -355,7 +354,7 @@ export default class DatasetController {
|
||||||
|
|
||||||
//store licenses:
|
//store licenses:
|
||||||
const licenses: number[] = request.input('licenses', []);
|
const licenses: number[] = request.input('licenses', []);
|
||||||
dataset.useTransaction(trx).related('licenses').sync(licenses);
|
await dataset.useTransaction(trx).related('licenses').sync(licenses);
|
||||||
|
|
||||||
// save authors and contributors
|
// save authors and contributors
|
||||||
await this.savePersons(dataset, request.input('authors', []), 'author', trx);
|
await this.savePersons(dataset, request.input('authors', []), 'author', trx);
|
||||||
|
@ -456,7 +455,7 @@ export default class DatasetController {
|
||||||
newFile.visibleInOai = true;
|
newFile.visibleInOai = true;
|
||||||
// let path = coverImage.filePath;
|
// let path = coverImage.filePath;
|
||||||
await dataset.useTransaction(trx).related('files').save(newFile);
|
await dataset.useTransaction(trx).related('files').save(newFile);
|
||||||
// await newFile.createHashValues();
|
await newFile.createHashValues(trx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,29 +681,39 @@ export default class DatasetController {
|
||||||
// throw new GeneralException(trans('exceptions.publish.release.update_error'));
|
// throw new GeneralException(trans('exceptions.publish.release.update_error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async edit({ params, inertia }) {
|
public async edit({ request, inertia, response }) {
|
||||||
const datasetQuery = Dataset.query().where('id', params.id);
|
const id = request.param('id');
|
||||||
datasetQuery.preload('titles').preload('descriptions').preload('coverage');
|
const datasetQuery = Dataset.query().where('id', id);
|
||||||
const dataset = await datasetQuery.firstOrFail();
|
datasetQuery
|
||||||
|
.preload('titles', (query) => query.orderBy('id', 'asc'))
|
||||||
|
.preload('descriptions', (query) => query.orderBy('id', 'asc'))
|
||||||
|
.preload('coverage')
|
||||||
|
.preload('licenses')
|
||||||
|
.preload('authors')
|
||||||
|
.preload('contributors')
|
||||||
|
.preload('subjects')
|
||||||
|
.preload('references')
|
||||||
|
.preload('files');
|
||||||
|
|
||||||
// await dataset.loadMany([
|
const dataset = await datasetQuery.firstOrFail();
|
||||||
// 'licenses',
|
const validStates = ['inprogress', 'rejected_editor'];
|
||||||
// 'authors',
|
if (!validStates.includes(dataset.server_state)) {
|
||||||
// 'contributors',
|
// session.flash('errors', 'Invalid server state!');
|
||||||
// 'titles',
|
return response
|
||||||
// 'abstracts',
|
.flash(
|
||||||
// 'files',
|
'warning',
|
||||||
// 'coverage',
|
`Invalid server state. Dataset with id ${id} cannot be edited. Datset has server state ${dataset.server_state}.`,
|
||||||
// 'subjects',
|
)
|
||||||
// 'references',
|
.redirect()
|
||||||
// ]);
|
.toRoute('dataset.list');
|
||||||
|
}
|
||||||
|
|
||||||
const titleTypes = Object.entries(TitleTypes)
|
const titleTypes = Object.entries(TitleTypes)
|
||||||
// .filter(([value]) => value !== 'Main')
|
.filter(([value]) => value !== 'Main')
|
||||||
.map(([key, value]) => ({ value: key, label: value }));
|
.map(([key, value]) => ({ value: key, label: value }));
|
||||||
|
|
||||||
const descriptionTypes = Object.entries(DescriptionTypes)
|
const descriptionTypes = Object.entries(DescriptionTypes)
|
||||||
// .filter(([value]) => value !== 'Abstract')
|
.filter(([value]) => value !== 'Abstract')
|
||||||
.map(([key, value]) => ({ value: key, label: value }));
|
.map(([key, value]) => ({ value: key, label: value }));
|
||||||
|
|
||||||
const languages = await Language.query().where('active', true).pluck('part1', 'part1');
|
const languages = await Language.query().where('active', true).pluck('part1', 'part1');
|
||||||
|
@ -724,33 +733,11 @@ export default class DatasetController {
|
||||||
const currentYear = currentDate.getFullYear();
|
const currentYear = currentDate.getFullYear();
|
||||||
const years = Array.from({ length: currentYear - 1990 + 1 }, (_, index) => 1990 + index);
|
const years = Array.from({ length: currentYear - 1990 + 1 }, (_, index) => 1990 + index);
|
||||||
|
|
||||||
// const licenses = await License.query()
|
const licenses = await License.query().select('id', 'name_long').where('active', 'true').pluck('name_long', 'id');
|
||||||
// .select('id', 'name_long', 'link_licence')
|
// const userHasRoles = user.roles;
|
||||||
// .orderBy('sort_order')
|
// const datasetHasLicenses = await dataset.related('licenses').query().pluck('id');
|
||||||
// .fetch();
|
|
||||||
const licenses = await License.query().select('id', 'name_long', 'link_licence').orderBy('sort_order');
|
|
||||||
|
|
||||||
// const checkeds = dataset.licenses.first().id;
|
// const checkeds = dataset.licenses.first().id;
|
||||||
|
|
||||||
const keywordTypes = {
|
|
||||||
uncontrolled: 'uncontrolled',
|
|
||||||
swd: 'swd',
|
|
||||||
};
|
|
||||||
|
|
||||||
const referenceTypes = ['DOI', 'Handle', 'ISBN', 'ISSN', 'URL', 'URN'];
|
|
||||||
|
|
||||||
const relationTypes = [
|
|
||||||
'IsSupplementTo',
|
|
||||||
'IsSupplementedBy',
|
|
||||||
'IsContinuedBy',
|
|
||||||
'Continues',
|
|
||||||
'IsNewVersionOf',
|
|
||||||
'IsPartOf',
|
|
||||||
'HasPart',
|
|
||||||
'Compiles',
|
|
||||||
'IsVariantFormOf',
|
|
||||||
];
|
|
||||||
|
|
||||||
const doctypes = {
|
const doctypes = {
|
||||||
analysisdata: { label: 'Analysis', value: 'analysisdata' },
|
analysisdata: { label: 'Analysis', value: 'analysisdata' },
|
||||||
measurementdata: { label: 'Measurements', value: 'measurementdata' },
|
measurementdata: { label: 'Measurements', value: 'measurementdata' },
|
||||||
|
@ -771,16 +758,164 @@ export default class DatasetController {
|
||||||
// messages,
|
// messages,
|
||||||
projects,
|
projects,
|
||||||
licenses,
|
licenses,
|
||||||
|
// datasetHasLicenses: Object.keys(datasetHasLicenses).map((key) => datasetHasLicenses[key]), //convert object to array with license ids
|
||||||
// checkeds,
|
// checkeds,
|
||||||
years,
|
years,
|
||||||
// languages,
|
// languages,
|
||||||
keywordTypes,
|
subjectTypes: SubjectTypes,
|
||||||
referenceTypes,
|
referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||||
relationTypes,
|
relationTypes: Object.entries(RelationTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||||
doctypes,
|
doctypes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async update({ request, response, session }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
// await request.validate({ schema: newDatasetSchema, messages: this.messages });
|
||||||
|
await request.validate(UpdateDatasetValidator);
|
||||||
|
} catch (error) {
|
||||||
|
// - Handle errors
|
||||||
|
// return response.badRequest(error.messages);
|
||||||
|
throw error;
|
||||||
|
// return response.badRequest(error.messages);
|
||||||
|
}
|
||||||
|
const id = request.param('id');
|
||||||
|
|
||||||
|
let trx: TransactionClientContract | null = null;
|
||||||
|
try {
|
||||||
|
trx = await Database.transaction();
|
||||||
|
// const user = (await User.find(auth.user?.id)) as User;
|
||||||
|
// await this.createDatasetAndAssociations(user, request, trx);
|
||||||
|
const dataset = await Dataset.findOrFail(id);
|
||||||
|
|
||||||
|
// save the licenses
|
||||||
|
const licenses: number[] = request.input('licenses', []);
|
||||||
|
// await dataset.useTransaction(trx).related('licenses').sync(licenses);
|
||||||
|
await dataset.useTransaction(trx).related('licenses').sync(licenses);
|
||||||
|
|
||||||
|
// save authors and contributors
|
||||||
|
await dataset.useTransaction(trx).related('authors').sync([]);
|
||||||
|
await dataset.useTransaction(trx).related('contributors').sync([]);
|
||||||
|
await this.savePersons(dataset, request.input('authors', []), 'author', trx);
|
||||||
|
await this.savePersons(dataset, request.input('contributors', []), 'contributor', trx);
|
||||||
|
|
||||||
|
//save the titles:
|
||||||
|
const titles = request.input('titles', []);
|
||||||
|
// const savedTitles:Array<Title> = [];
|
||||||
|
for (const titleData of titles) {
|
||||||
|
if (titleData.id) {
|
||||||
|
const title = await Title.findOrFail(titleData.id);
|
||||||
|
title.value = titleData.value;
|
||||||
|
title.language = titleData.language;
|
||||||
|
title.type = titleData.type;
|
||||||
|
if (title.$isDirty) {
|
||||||
|
await title.useTransaction(trx).save();
|
||||||
|
// await dataset.useTransaction(trx).related('titles').save(title);
|
||||||
|
// savedTitles.push(title);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const title = new Title();
|
||||||
|
title.fill(titleData);
|
||||||
|
// savedTitles.push(title);
|
||||||
|
await dataset.useTransaction(trx).related('titles').save(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the abstracts
|
||||||
|
const descriptions = request.input('descriptions', []);
|
||||||
|
// const savedTitles:Array<Title> = [];
|
||||||
|
for (const descriptionData of descriptions) {
|
||||||
|
if (descriptionData.id) {
|
||||||
|
const description = await Description.findOrFail(descriptionData.id);
|
||||||
|
description.value = descriptionData.value;
|
||||||
|
description.language = descriptionData.language;
|
||||||
|
description.type = descriptionData.type;
|
||||||
|
if (description.$isDirty) {
|
||||||
|
await description.useTransaction(trx).save();
|
||||||
|
// await dataset.useTransaction(trx).related('titles').save(title);
|
||||||
|
// savedTitles.push(title);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const description = new Description();
|
||||||
|
description.fill(descriptionData);
|
||||||
|
// savedTitles.push(title);
|
||||||
|
await dataset.useTransaction(trx).related('descriptions').save(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save already existing files
|
||||||
|
const files = request.input('fileInputs', []);
|
||||||
|
for (const fileData of files) {
|
||||||
|
if (fileData.id) {
|
||||||
|
const file = await File.findOrFail(fileData.id);
|
||||||
|
file.label = fileData.label;
|
||||||
|
file.sortOrder = fileData.sort_order;
|
||||||
|
if (file.$isDirty) {
|
||||||
|
await file.useTransaction(trx).save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle new uploaded files:
|
||||||
|
const uploadedFiles = request.files('files');
|
||||||
|
if (Array.isArray(uploadedFiles) && uploadedFiles.length > 0) {
|
||||||
|
// let index = 1;
|
||||||
|
// for (const key in files) {
|
||||||
|
// const formFile = files[key]
|
||||||
|
// for (const fileData of files) {
|
||||||
|
for (const [index, fileData] of uploadedFiles.entries()) {
|
||||||
|
// const uploads = request.file('uploads');
|
||||||
|
// const fileIndex = formFile.file;
|
||||||
|
// const file = uploads[fileIndex];
|
||||||
|
|
||||||
|
const fileName = `file-${cuid()}.${fileData.extname}`;
|
||||||
|
const mimeType = fileData.headers['content-type'] || 'application/octet-stream'; // Fallback to a default MIME type
|
||||||
|
const datasetFolder = `files/${dataset.id}`;
|
||||||
|
await fileData.moveToDisk(
|
||||||
|
datasetFolder,
|
||||||
|
{
|
||||||
|
name: fileName,
|
||||||
|
overwrite: true, // overwrite in case of conflict
|
||||||
|
},
|
||||||
|
'local',
|
||||||
|
);
|
||||||
|
// save file metadata into db
|
||||||
|
const newFile = new File();
|
||||||
|
newFile.pathName = `${datasetFolder}/${fileName}`;
|
||||||
|
newFile.fileSize = fileData.size;
|
||||||
|
newFile.mimeType = mimeType;
|
||||||
|
newFile.label = fileData.clientName;
|
||||||
|
newFile.sortOrder = index;
|
||||||
|
newFile.visibleInFrontdoor = true;
|
||||||
|
newFile.visibleInOai = true;
|
||||||
|
// let path = coverImage.filePath;
|
||||||
|
await dataset.useTransaction(trx).related('files').save(newFile);
|
||||||
|
await newFile.createHashValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = request.only(['project_id', 'embargo_date', 'language', 'type', 'creating_corporation']);
|
||||||
|
// dataset.type = request.input('type');
|
||||||
|
dataset.merge(input);
|
||||||
|
// let test: boolean = dataset.$isDirty;
|
||||||
|
await dataset.useTransaction(trx).save();
|
||||||
|
|
||||||
|
await trx.commit();
|
||||||
|
console.log('Dataset and related models created successfully');
|
||||||
|
} catch (error) {
|
||||||
|
if (trx !== null) {
|
||||||
|
await trx.rollback();
|
||||||
|
}
|
||||||
|
console.error('Failed to create dataset and related models:', error);
|
||||||
|
// throw new ValidationException(true, { 'upload error': `failed to create dataset and related models. ${error}` });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.flash('message', 'Dataset has been created successfully');
|
||||||
|
// return response.redirect().toRoute('user.index');
|
||||||
|
return response.redirect().back();
|
||||||
|
}
|
||||||
|
|
||||||
public async delete({ request, inertia, response, session }) {
|
public async delete({ request, inertia, response, session }) {
|
||||||
const id = request.param('id');
|
const id = request.param('id');
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -9,6 +9,11 @@ export default class Description extends BaseModel {
|
||||||
public static timestamps = false;
|
public static timestamps = false;
|
||||||
public static fillable: string[] = ['value', 'type', 'language'];
|
public static fillable: string[] = ['value', 'type', 'language'];
|
||||||
|
|
||||||
|
@column({
|
||||||
|
isPrimary: true,
|
||||||
|
})
|
||||||
|
public id: number;
|
||||||
|
|
||||||
@column({})
|
@column({})
|
||||||
public document_id: number;
|
public document_id: number;
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import {
|
import { column, hasMany, HasMany, belongsTo, BelongsTo, SnakeCaseNamingStrategy, computed } from '@ioc:Adonis/Lucid/Orm';
|
||||||
column,
|
|
||||||
hasMany,
|
|
||||||
HasMany,
|
|
||||||
belongsTo,
|
|
||||||
BelongsTo,
|
|
||||||
// manyToMany,
|
|
||||||
// ManyToMany,
|
|
||||||
SnakeCaseNamingStrategy,
|
|
||||||
} from '@ioc:Adonis/Lucid/Orm';
|
|
||||||
import HashValue from './HashValue';
|
import HashValue from './HashValue';
|
||||||
import Dataset from './Dataset';
|
import Dataset from './Dataset';
|
||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
|
// import { Buffer } from 'buffer';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
|
||||||
|
|
||||||
export default class File extends BaseModel {
|
export default class File extends BaseModel {
|
||||||
|
// private readonly _data: Uint8Array;
|
||||||
|
// private readonly _type: string;
|
||||||
|
// private readonly _size: number;
|
||||||
|
|
||||||
public static namingStrategy = new SnakeCaseNamingStrategy();
|
public static namingStrategy = new SnakeCaseNamingStrategy();
|
||||||
public static primaryKey = 'id';
|
public static primaryKey = 'id';
|
||||||
public static table = 'document_files';
|
public static table = 'document_files';
|
||||||
|
@ -73,4 +72,93 @@ export default class File extends BaseModel {
|
||||||
foreignKey: 'file_id',
|
foreignKey: 'file_id',
|
||||||
})
|
})
|
||||||
public hashvalues: HasMany<typeof HashValue>;
|
public hashvalues: HasMany<typeof HashValue>;
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'filePath',
|
||||||
|
})
|
||||||
|
public get filePath() {
|
||||||
|
return `/storage/app/public/${this.pathName}`;
|
||||||
|
// const mainTitle = this.titles?.find((title) => title.type === 'Main');
|
||||||
|
// return mainTitle ? mainTitle.value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'size',
|
||||||
|
})
|
||||||
|
public get size() {
|
||||||
|
return this.fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'type',
|
||||||
|
})
|
||||||
|
public get type() {
|
||||||
|
return this.mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'name',
|
||||||
|
})
|
||||||
|
get name(): string {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'lastModified',
|
||||||
|
})
|
||||||
|
get lastModified(): number {
|
||||||
|
return this.updatedAt.toUnixInteger(); //.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly webkitRelativePath: string = '';
|
||||||
|
|
||||||
|
@computed({
|
||||||
|
serializeAs: 'fileData',
|
||||||
|
})
|
||||||
|
public get fileData(): string {
|
||||||
|
// return this.fileData;
|
||||||
|
// const fileData = fs.readFileSync(path.resolve(__dirname, this.filePath));
|
||||||
|
// const fileData = fs.readFileSync(this.filePath);
|
||||||
|
const fileContent: Buffer = fs.readFileSync(this.filePath);
|
||||||
|
// Create a Blob from the file content
|
||||||
|
// const blob = new Blob([fileContent], { type: this.type }); // Adjust
|
||||||
|
// let fileSrc = URL.createObjectURL(blob);
|
||||||
|
// return fileSrc;
|
||||||
|
|
||||||
|
// return Buffer.from(fileContent);
|
||||||
|
// get the buffer from somewhere
|
||||||
|
// const buff = fs.readFileSync('./test.bin');
|
||||||
|
// create a JSON string that contains the data in the property "blob"
|
||||||
|
const json = JSON.stringify({ blob: fileContent.toString('base64') });
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createHashValues(trx?: TransactionClientContract) {
|
||||||
|
const hashtypes: string[] = ['md5', 'sha512'];
|
||||||
|
|
||||||
|
for (const type of hashtypes) {
|
||||||
|
const hash = new HashValue();
|
||||||
|
hash.type = type;
|
||||||
|
const hashString = await this.checksumFile(this.filePath, type); // Assuming getRealHash is a method in the same model
|
||||||
|
hash.value = hashString;
|
||||||
|
|
||||||
|
// https://github.com/adonisjs/core/discussions/1872#discussioncomment-132289
|
||||||
|
const file: File = this;
|
||||||
|
if (trx) {
|
||||||
|
await file.useTransaction(trx).related('hashvalues').save(hash); // Save the hash value to the database
|
||||||
|
} else {
|
||||||
|
await file.related('hashvalues').save(hash); // Save the hash value to the database
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checksumFile(path, hashName = 'md5'): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const hash = crypto.createHash(hashName);
|
||||||
|
const stream = fs.createReadStream(path);
|
||||||
|
stream.on('error', (err) => reject(err));
|
||||||
|
stream.on('data', (chunk) => hash.update(chunk));
|
||||||
|
stream.on('end', () => resolve(hash.digest('hex')));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import File from './File';
|
||||||
|
|
||||||
export default class HashValue extends BaseModel {
|
export default class HashValue extends BaseModel {
|
||||||
public static namingStrategy = new SnakeCaseNamingStrategy();
|
public static namingStrategy = new SnakeCaseNamingStrategy();
|
||||||
public static primaryKey = 'file_id, type';
|
// public static primaryKey = 'file_id,type';
|
||||||
public static table = 'file_hashvalues';
|
public static table = 'file_hashvalues';
|
||||||
|
|
||||||
// static get primaryKey () {
|
// static get primaryKey () {
|
||||||
|
@ -20,10 +20,10 @@ export default class HashValue extends BaseModel {
|
||||||
// public id: number;
|
// public id: number;
|
||||||
|
|
||||||
// Foreign key is still on the same model
|
// Foreign key is still on the same model
|
||||||
@column({})
|
@column({ isPrimary: true })
|
||||||
public file_id: number;
|
public file_id: number;
|
||||||
|
|
||||||
@column({})
|
@column({ isPrimary: true })
|
||||||
public type: string;
|
public type: string;
|
||||||
|
|
||||||
@column()
|
@column()
|
||||||
|
|
|
@ -10,6 +10,11 @@ export default class Title extends BaseModel {
|
||||||
public static timestamps = false;
|
public static timestamps = false;
|
||||||
public static fillable: string[] = ['value', 'type', 'language'];
|
public static fillable: string[] = ['value', 'type', 'language'];
|
||||||
|
|
||||||
|
@column({
|
||||||
|
isPrimary: true,
|
||||||
|
})
|
||||||
|
public id: number;
|
||||||
|
|
||||||
@column({})
|
@column({})
|
||||||
public document_id: number;
|
public document_id: number;
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ export default class CreateDatasetValidator {
|
||||||
'unique': '{{ field }} must be unique, and this value is already taken',
|
'unique': '{{ field }} must be unique, and this value is already taken',
|
||||||
// 'confirmed': '{{ field }} is not correct',
|
// 'confirmed': '{{ field }} is not correct',
|
||||||
'licenses.minLength': 'at least {{ options.minLength }} permission must be defined',
|
'licenses.minLength': 'at least {{ options.minLength }} permission must be defined',
|
||||||
'licenses.*.number': 'Define roles as valid numbers',
|
'licenses.*.number': 'Define licences as valid numbers',
|
||||||
'rights.equalTo': 'you must agree to continue',
|
'rights.equalTo': 'you must agree to continue',
|
||||||
|
|
||||||
'titles.0.value.minLength': 'Main Title must be at least {{ options.minLength }} characters long',
|
'titles.0.value.minLength': 'Main Title must be at least {{ options.minLength }} characters long',
|
||||||
|
|
179
app/Validators/UpdateDatasetValidator.ts
Normal file
179
app/Validators/UpdateDatasetValidator.ts
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator';
|
||||||
|
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { TitleTypes, DescriptionTypes, RelationTypes, ReferenceIdentifierTypes, ContributorTypes } from 'Contracts/enums';
|
||||||
|
|
||||||
|
export default class UpdateDatasetValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* 1. The username must be of data type string. But then also, it should
|
||||||
|
* not contain special characters or numbers.
|
||||||
|
* ```
|
||||||
|
* schema.string({}, [ rules.alpha() ])
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 2. The email must be of data type string, formatted as a valid
|
||||||
|
* email. But also, not used by any other user.
|
||||||
|
* ```
|
||||||
|
* schema.string({}, [
|
||||||
|
* rules.email(),
|
||||||
|
* rules.unique({ table: 'users', column: 'email' }),
|
||||||
|
* ])
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public schema = schema.create({
|
||||||
|
// first step
|
||||||
|
language: schema.string({ trim: true }, [
|
||||||
|
rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores
|
||||||
|
]),
|
||||||
|
licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset
|
||||||
|
rights: schema.string([rules.equalTo('true')]),
|
||||||
|
// second step
|
||||||
|
type: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
|
||||||
|
creating_corporation: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
|
||||||
|
titles: schema.array([rules.minLength(1)]).members(
|
||||||
|
schema.object().members({
|
||||||
|
value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
|
||||||
|
type: schema.enum(Object.values(TitleTypes)),
|
||||||
|
language: schema.string({ trim: true }, [
|
||||||
|
rules.minLength(2),
|
||||||
|
rules.maxLength(255),
|
||||||
|
rules.translatedLanguage('/language', 'type'),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
descriptions: schema.array([rules.minLength(1)]).members(
|
||||||
|
schema.object().members({
|
||||||
|
value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
|
||||||
|
type: schema.enum(Object.values(DescriptionTypes)),
|
||||||
|
language: schema.string({ trim: true }, [
|
||||||
|
rules.minLength(2),
|
||||||
|
rules.maxLength(255),
|
||||||
|
rules.translatedLanguage('/language', 'type'),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
authors: schema.array([rules.minLength(1)]).members(schema.object().members({ email: schema.string({ trim: true }) })),
|
||||||
|
contributors: schema.array.optional().members(
|
||||||
|
schema.object().members({
|
||||||
|
email: schema.string({ trim: true }),
|
||||||
|
pivot_contributor_type: schema.enum(Object.keys(ContributorTypes)),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
// third step
|
||||||
|
project_id: schema.number.optional(),
|
||||||
|
embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]),
|
||||||
|
coverage: schema.object().members({
|
||||||
|
x_min: schema.number(),
|
||||||
|
x_max: schema.number(),
|
||||||
|
y_min: schema.number(),
|
||||||
|
y_max: schema.number(),
|
||||||
|
elevation_absolut: schema.number.optional(),
|
||||||
|
elevation_min: schema.number.optional([rules.requiredIfExists('elevation_max')]),
|
||||||
|
elevation_max: schema.number.optional([rules.requiredIfExists('elevation_min')]),
|
||||||
|
depth_absolut: schema.number.optional(),
|
||||||
|
depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]),
|
||||||
|
depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]),
|
||||||
|
}),
|
||||||
|
references: schema.array.optional([rules.uniqueArray('value')]).members(
|
||||||
|
schema.object().members({
|
||||||
|
value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
|
||||||
|
type: schema.enum(Object.values(ReferenceIdentifierTypes)),
|
||||||
|
relation: schema.enum(Object.values(RelationTypes)),
|
||||||
|
label: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members(
|
||||||
|
schema.object().members({
|
||||||
|
value: schema.string({ trim: true }, [
|
||||||
|
rules.minLength(3),
|
||||||
|
rules.maxLength(255),
|
||||||
|
// rules.unique({ table: 'dataset_subjects', column: 'value' }),
|
||||||
|
]),
|
||||||
|
// type: schema.enum(Object.values(TitleTypes)),
|
||||||
|
language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
// file: schema.file({
|
||||||
|
// size: '100mb',
|
||||||
|
// extnames: ['jpg', 'gif', 'png'],
|
||||||
|
// }),
|
||||||
|
files: schema.array.optional().members(
|
||||||
|
schema.file({
|
||||||
|
size: '100mb',
|
||||||
|
extnames: ['jpg', 'gif', 'png', 'tif', 'pdf'],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// upload: schema.object().members({
|
||||||
|
// label: schema.string({ trim: true }, [rules.maxLength(255)]),
|
||||||
|
|
||||||
|
// // label: schema.string({ trim: true }, [
|
||||||
|
// // // rules.minLength(3),
|
||||||
|
// // // rules.maxLength(255),
|
||||||
|
// // ]),
|
||||||
|
// }),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom messages for validation failures. You can make use of dot notation `(.)`
|
||||||
|
* for targeting nested fields and array expressions `(*)` for targeting all
|
||||||
|
* children of an array. For example:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* 'profile.username.required': 'Username is required',
|
||||||
|
* 'scores.*.number': 'Define scores as valid numbers'
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public messages: CustomMessages = {
|
||||||
|
'minLength': '{{ field }} must be at least {{ options.minLength }} characters long',
|
||||||
|
'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long',
|
||||||
|
'required': '{{ field }} is required',
|
||||||
|
'unique': '{{ field }} must be unique, and this value is already taken',
|
||||||
|
// 'confirmed': '{{ field }} is not correct',
|
||||||
|
'licenses.minLength': 'at least {{ options.minLength }} permission must be defined',
|
||||||
|
'licenses.*.number': 'Define licences as valid numbers',
|
||||||
|
'rights.equalTo': 'you must agree to continue',
|
||||||
|
|
||||||
|
'titles.0.value.minLength': 'Main Title must be at least {{ options.minLength }} characters long',
|
||||||
|
'titles.0.value.required': 'Main Title is required',
|
||||||
|
'titles.*.value.required': 'Additional title is required, if defined',
|
||||||
|
'titles.*.type.required': 'Additional title type is required',
|
||||||
|
'titles.*.language.required': 'Additional title language is required',
|
||||||
|
'titles.*.language.translatedLanguage': 'The language of the translated title must be different from the language of the dataset',
|
||||||
|
|
||||||
|
'descriptions.0.value.minLength': 'Main Abstract must be at least {{ options.minLength }} characters long',
|
||||||
|
'descriptions.0.value.required': 'Main Abstract is required',
|
||||||
|
'descriptions.*.value.required': 'Additional description is required, if defined',
|
||||||
|
'descriptions.*.type.required': 'Additional description type is required',
|
||||||
|
'descriptions.*.language.required': 'Additional description language is required',
|
||||||
|
'descriptions.*.language.translatedLanguage':
|
||||||
|
'The language of the translated description must be different from the language of the dataset',
|
||||||
|
|
||||||
|
'authors.minLength': 'at least {{ options.minLength }} author must be defined',
|
||||||
|
'contributors.*.pivot_contributor_type.required': 'contributor type is required, if defined',
|
||||||
|
|
||||||
|
'after': `{{ field }} must be older than ${dayjs().add(10, 'day')}`,
|
||||||
|
|
||||||
|
'subjects.minLength': 'at least {{ options.minLength }} keywords must be defined',
|
||||||
|
'subjects.uniqueArray': 'The {{ options.array }} array must have unique values based on the {{ options.field }} attribute.',
|
||||||
|
'subjects.*.value.required': 'keyword value is required',
|
||||||
|
'subjects.*.value.minLength': 'keyword value must be at least {{ options.minLength }} characters long',
|
||||||
|
'subjects.*.type.required': 'keyword type is required',
|
||||||
|
'subjects.*.language.required': 'language of keyword is required',
|
||||||
|
|
||||||
|
'references.*.value.required': 'Additional reference value is required, if defined',
|
||||||
|
'references.*.type.required': 'Additional reference identifier type is required',
|
||||||
|
'references.*.relation.required': 'Additional reference relation type is required',
|
||||||
|
'references.*.label.required': 'Additional reference label is required',
|
||||||
|
|
||||||
|
'files.minLength': 'At least {{ options.minLength }} file upload is required.',
|
||||||
|
'files.*.size': 'file size is to big',
|
||||||
|
'files.extnames': 'file extension is not supported',
|
||||||
|
};
|
||||||
|
}
|
1279
package-lock.json
generated
1279
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,7 @@
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"babel-preset-typescript-vue3": "^2.0.17",
|
"babel-preset-typescript-vue3": "^2.0.17",
|
||||||
"chart.js": "^4.2.0",
|
"chart.js": "^4.2.0",
|
||||||
|
"dotenv-webpack": "^8.0.1",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.32.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-adonis": "^2.1.1",
|
"eslint-plugin-adonis": "^2.1.1",
|
||||||
|
|
|
@ -1,34 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<section
|
<section aria-label="File Upload Modal"
|
||||||
aria-label="File Upload Modal"
|
|
||||||
class="relative h-full flex flex-col bg-white dark:bg-slate-900/70 shadow-xl rounded-md"
|
class="relative h-full flex flex-col bg-white dark:bg-slate-900/70 shadow-xl rounded-md"
|
||||||
v-on:dragenter="dragEnterHandler"
|
v-on:dragenter="dragEnterHandler" v-on:dragleave="dragLeaveHandler" v-on:dragover="dragOverHandler"
|
||||||
v-on:dragleave="dragLeaveHandler"
|
v-on:drop="dropHandler">
|
||||||
v-on:dragover="dragOverHandler"
|
|
||||||
v-on:drop="dropHandler"
|
|
||||||
>
|
|
||||||
<!-- ondrop="dropHandler(event);"
|
<!-- ondrop="dropHandler(event);"
|
||||||
ondragover="dragOverHandler(event);"
|
ondragover="dragOverHandler(event);"
|
||||||
ondragleave="dragLeaveHandler(event);"
|
ondragleave="dragLeaveHandler(event);"
|
||||||
ondragenter="dragEnterHandler(event);" -->
|
ondragenter="dragEnterHandler(event);" -->
|
||||||
|
|
||||||
<!-- overlay -->
|
<!-- overlay -->
|
||||||
<div
|
<div id="overlay" ref="overlay"
|
||||||
id="overlay"
|
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md">
|
||||||
ref="overlay"
|
|
||||||
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md"
|
|
||||||
>
|
|
||||||
<i>
|
<i>
|
||||||
<svg
|
<svg class="fill-current w-12 h-12 mb-3 text-blue-700" xmlns="http://www.w3.org/2000/svg" width="24"
|
||||||
class="fill-current w-12 h-12 mb-3 text-blue-700"
|
height="24" viewBox="0 0 24 24">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z"
|
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z" />
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<p class="text-lg text-blue-700">Drop files to upload</p>
|
<p class="text-lg text-blue-700">Drop files to upload</p>
|
||||||
|
@ -46,25 +33,14 @@
|
||||||
</button>
|
</button>
|
||||||
</header> -->
|
</header> -->
|
||||||
<header class="flex items-center justify-center w-full">
|
<header class="flex items-center justify-center w-full">
|
||||||
<label
|
<label for="dropzone-file"
|
||||||
for="dropzone-file"
|
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
|
||||||
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
||||||
<svg
|
<svg aria-hidden="true" class="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor"
|
||||||
aria-hidden="true"
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
class="w-10 h-10 mb-3 text-gray-400"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
fill="none"
|
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12">
|
||||||
stroke="currentColor"
|
</path>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
|
||||||
></path>
|
|
||||||
</svg>
|
</svg>
|
||||||
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
<span class="font-semibold">Click to upload</span> or drag and drop
|
<span class="font-semibold">Click to upload</span> or drag and drop
|
||||||
|
@ -78,7 +54,8 @@
|
||||||
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">To Upload</h1>
|
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">To Upload</h1>
|
||||||
|
|
||||||
<!-- <ul id="gallery" class="flex flex-1 flex-wrap -m-1"> -->
|
<!-- <ul id="gallery" class="flex flex-1 flex-wrap -m-1"> -->
|
||||||
<draggable id="gallery" tag="ul" class="flex flex-1 flex-wrap -m-1" v-model="files" item-key="sorting">
|
|
||||||
|
<draggable id="galleryxy" tag="ul" class="flex flex-1 flex-wrap -m-1" v-model="items" item-key="sort_order">
|
||||||
<!-- <li
|
<!-- <li
|
||||||
v-if="files.length == 0"
|
v-if="files.length == 0"
|
||||||
id="empty"
|
id="empty"
|
||||||
|
@ -108,17 +85,12 @@
|
||||||
<span class="text-small text-gray-500">No files selected</span>
|
<span class="text-small text-gray-500">No files selected</span>
|
||||||
</li> -->
|
</li> -->
|
||||||
<template #item="{ index, element }">
|
<template #item="{ index, element }">
|
||||||
<li class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24">
|
<li class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24" :key="index">
|
||||||
<article
|
<article v-if="element.type.match('image.*')" tabindex="0"
|
||||||
v-if="element.type.match('image.*')"
|
class="bg-gray-50 group hasImage w-full h-full rounded-md cursor-pointer relative text-transparent hover:text-white shadow-sm">
|
||||||
tabindex="0"
|
<!-- :src="element.fileSrc" :src="generateURL(element)" -->
|
||||||
class="bg-gray-50 group hasImage w-full h-full rounded-md cursor-pointer relative text-transparent hover:text-white shadow-sm"
|
<img :alt="element.name" :src="element.fileSrc"
|
||||||
>
|
class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed opacity-75" />
|
||||||
<img
|
|
||||||
:alt="element.name"
|
|
||||||
:src="generateURL(element)"
|
|
||||||
class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed opacity-75"
|
|
||||||
/>
|
|
||||||
<!-- <section
|
<!-- <section
|
||||||
class="hasError text-red-500 shadow-sm font-semibold flex flex-row rounded-md text-xs break-words w-full h-full z-21 absolute top-0 py-2 px-3"
|
class="hasError text-red-500 shadow-sm font-semibold flex flex-row rounded-md text-xs break-words w-full h-full z-21 absolute top-0 py-2 px-3"
|
||||||
>
|
>
|
||||||
|
@ -132,15 +104,14 @@
|
||||||
<DeleteIcon></DeleteIcon>
|
<DeleteIcon></DeleteIcon>
|
||||||
</button>
|
</button>
|
||||||
</section> -->
|
</section> -->
|
||||||
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
<section
|
||||||
|
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
||||||
<h1 class="flex-1">{{ element.name }}</h1>
|
<h1 class="flex-1">{{ element.name }}</h1>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<p class="p-1 size text-xs">{{ getFileSize(element) }}</p>
|
<p class="p-1 size text-xs">{{ getFileSize(element) }}</p>
|
||||||
<p class="p-1 size text-xs text-gray-700">{{ index }}</p>
|
<p class="p-1 size text-xs text-gray-700">{{ element.sort_order }}</p>
|
||||||
<button
|
<button class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
|
||||||
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
|
@click="removeFile(index)">
|
||||||
@click="removeFile(index)"
|
|
||||||
>
|
|
||||||
<DeleteIcon></DeleteIcon>
|
<DeleteIcon></DeleteIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -150,16 +121,17 @@
|
||||||
</div> -->
|
</div> -->
|
||||||
</article>
|
</article>
|
||||||
<!-- :class="errors && errors[`files.${index}`] ? 'bg-red-400' : 'bg-gray-100'" -->
|
<!-- :class="errors && errors[`files.${index}`] ? 'bg-red-400' : 'bg-gray-100'" -->
|
||||||
<article v-else tabindex="0" class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
|
<article v-else tabindex="0"
|
||||||
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
|
||||||
|
<section
|
||||||
|
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
||||||
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800">{{ element.name }}</h1>
|
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800">{{ element.name }}</h1>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<p class="p-1 size text-xs text-gray-700">{{ getFileSize(element) }}</p>
|
<p class="p-1 size text-xs text-gray-700">{{ getFileSize(element) }}</p>
|
||||||
<p class="p-1 size text-xs text-gray-700">{{ index }}</p>
|
<p class="p-1 size text-xs text-gray-700">{{ element.sort_order }}</p>
|
||||||
<button
|
<button
|
||||||
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
|
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
|
||||||
@click="removeFile(index)"
|
@click="removeFile(index)">
|
||||||
>
|
|
||||||
<DeleteIcon></DeleteIcon>
|
<DeleteIcon></DeleteIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -191,11 +163,8 @@
|
||||||
|
|
||||||
<!-- sticky footer -->
|
<!-- sticky footer -->
|
||||||
<footer class="flex justify-end px-8 pb-8 pt-4">
|
<footer class="flex justify-end px-8 pb-8 pt-4">
|
||||||
<button
|
<button id="cancel" class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none"
|
||||||
id="cancel"
|
@click="clearAllFiles">
|
||||||
class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none"
|
|
||||||
@click="clearAllFiles"
|
|
||||||
>
|
|
||||||
Clear
|
Clear
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -203,13 +172,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Prop, Ref } from 'vue-facing-decorator';
|
import { Component, Vue, Prop, Ref, Watch } from 'vue-facing-decorator';
|
||||||
// import BaseButton from './BaseButton.vue';
|
|
||||||
import { usePage } from '@inertiajs/vue3';
|
import { usePage } from '@inertiajs/vue3';
|
||||||
import DeleteIcon from '@/Components/Icons/Delete.vue';
|
import DeleteIcon from '@/Components/Icons/Delete.vue';
|
||||||
// import { Page, PageProps, Errors, ErrorBag } from '@inertiajs/inertia';
|
// import { Page, PageProps, Errors, ErrorBag } from '@inertiajs/inertia';
|
||||||
import Draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import { TestFile } from '@/Dataset';
|
import { Buffer } from 'buffer';
|
||||||
|
import { TethysFile } from '@/Dataset';
|
||||||
|
|
||||||
|
// lastModified: 1691759507591
|
||||||
|
// lastModifiedDate: Fri Aug 11 2023 15:11:47 GMT+0200 (Mitteleuropäische Sommerzeit)
|
||||||
|
// name: 'freieIP.png'
|
||||||
|
// size: 112237
|
||||||
|
// type: 'image/png'
|
||||||
|
// webkitRelativePath: ''
|
||||||
|
|
||||||
interface IDictionary {
|
interface IDictionary {
|
||||||
[index: string]: Array<string>;
|
[index: string]: Array<string>;
|
||||||
|
@ -239,7 +215,7 @@ interface InteriaPage {
|
||||||
Draggable,
|
Draggable,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class FileUploadComponent extends Vue {
|
class FileUploadComponent extends Vue {
|
||||||
/**
|
/**
|
||||||
* Connect map id.
|
* Connect map id.
|
||||||
*/
|
*/
|
||||||
|
@ -255,22 +231,45 @@ export default class FileUploadComponent extends Vue {
|
||||||
// @Prop() files: Array<TestFile>;
|
// @Prop() files: Array<TestFile>;
|
||||||
|
|
||||||
@Prop({
|
@Prop({
|
||||||
type: Array<TestFile>,
|
type: Array<File>,
|
||||||
default: [],
|
default: [],
|
||||||
})
|
})
|
||||||
modelValue: Array<TestFile>;
|
files: Array<TethysFile | File>;
|
||||||
// mdiTrashCan = mdiTrashCan;
|
|
||||||
|
|
||||||
get files() {
|
get items(): Array<TethysFile | File> {
|
||||||
return this.modelValue;
|
return this.files;
|
||||||
}
|
}
|
||||||
set files(value: Array<TestFile>) {
|
set items(values: Array<TethysFile | File>) {
|
||||||
// this.modelValue = value;
|
// this.modelValue = value;
|
||||||
this.modelValue.length = 0;
|
this.files.length = 0;
|
||||||
this.modelValue.push(...value);
|
this.files.push(...values);
|
||||||
|
// values.forEach((item, index) => {
|
||||||
|
// item.sort_order = index + 1; // Assuming sort_order starts from 1
|
||||||
|
// this.files.push(item);
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
dragEnterHandler(e) {
|
@Watch("files", {
|
||||||
|
deep: true
|
||||||
|
})
|
||||||
|
public propertyWatcher(newItems: Array<TethysFile>) {
|
||||||
|
// Update sort_order based on the new index when the list is changed
|
||||||
|
newItems.forEach((item, index) => {
|
||||||
|
item.sort_order = index + 1; // Assuming sort_order starts from 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public created() {
|
||||||
|
for (const file of this.files) {
|
||||||
|
if (!(file instanceof File)) {
|
||||||
|
// console.log(`${file.name} path is ${file.filePath} here.`);
|
||||||
|
this.generateURL(file);
|
||||||
|
// console.log(`${file.fileSrc} path.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dragEnterHandler(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this._hasFiles(e.dataTransfer)) {
|
if (!this._hasFiles(e.dataTransfer)) {
|
||||||
return;
|
return;
|
||||||
|
@ -278,17 +277,17 @@ export default class FileUploadComponent extends Vue {
|
||||||
++this.counter && this.overlay.classList.add('draggedover');
|
++this.counter && this.overlay.classList.add('draggedover');
|
||||||
}
|
}
|
||||||
|
|
||||||
dragLeaveHandler() {
|
public dragLeaveHandler() {
|
||||||
1 > --this.counter && this.overlay.classList.remove('draggedover');
|
1 > --this.counter && this.overlay.classList.remove('draggedover');
|
||||||
}
|
}
|
||||||
|
|
||||||
dragOverHandler(e) {
|
public dragOverHandler(e) {
|
||||||
if (this._hasFiles(e.dataTransfer)) {
|
if (this._hasFiles(e.dataTransfer)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startDrag(evt, item) {
|
public startDrag(evt, item) {
|
||||||
evt.dataTransfer.dropEffect = 'move';
|
evt.dataTransfer.dropEffect = 'move';
|
||||||
evt.dataTransfer.effectAllowed = 'move';
|
evt.dataTransfer.effectAllowed = 'move';
|
||||||
evt.dataTransfer.setData('itemID', item.id);
|
evt.dataTransfer.setData('itemID', item.id);
|
||||||
|
@ -296,28 +295,31 @@ export default class FileUploadComponent extends Vue {
|
||||||
|
|
||||||
// reset counter and append file to gallery when file is dropped
|
// reset counter and append file to gallery when file is dropped
|
||||||
|
|
||||||
dropHandler(event) {
|
public dropHandler(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
for (const file of event.dataTransfer.files) {
|
for (const file of event.dataTransfer.files) {
|
||||||
// let fileName = String(file.name.replace(/\.[^/.]+$/, ''));
|
// let fileName = String(file.name.replace(/\.[^/.]+$/, ''));
|
||||||
// file.label = fileName;
|
// file.label = fileName;
|
||||||
|
// if (file.type.match('image.*')) {
|
||||||
|
// this.generateURL(file);
|
||||||
|
// }
|
||||||
this._addFile(file);
|
this._addFile(file);
|
||||||
}
|
}
|
||||||
this.overlay.classList.remove('draggedover');
|
this.overlay.classList.remove('draggedover');
|
||||||
this.counter = 0;
|
this.counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFile(event) {
|
public onChangeFile(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// let uploadedFile = event.target.files[0];
|
// let uploadedFile = event.target.files[0];
|
||||||
|
|
||||||
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
||||||
// form.file = event.target.files[0];
|
|
||||||
// form.upload.label = fileName;
|
|
||||||
// console.log(file.file);
|
|
||||||
for (const file of event.target.files) {
|
for (const file of event.target.files) {
|
||||||
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
||||||
// file.label = fileName;
|
// file.label = fileName;
|
||||||
|
// if (file.type.match('image.*')) {
|
||||||
|
// this.generateURL(file);
|
||||||
|
// }
|
||||||
this._addFile(file);
|
this._addFile(file);
|
||||||
}
|
}
|
||||||
// this.overlay.classList.remove('draggedover');
|
// this.overlay.classList.remove('draggedover');
|
||||||
|
@ -341,24 +343,58 @@ export default class FileUploadComponent extends Vue {
|
||||||
return Object.fromEntries(Object.entries(this.errors).filter(([key]) => key.startsWith('file')));
|
return Object.fromEntries(Object.entries(this.errors).filter(([key]) => key.startsWith('file')));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAllFiles(event) {
|
public clearAllFiles(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.files.splice(0);
|
this.items.splice(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFile(key) {
|
public removeFile(key) {
|
||||||
this.files.splice(key, 1);
|
this.items.splice(key, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateURL(file) {
|
public generateURL(file: TethysFile | File): string {
|
||||||
let fileSrc = URL.createObjectURL(file);
|
// const arrayBuffer = Buffer.from(file.fileData.data);
|
||||||
setTimeout(() => {
|
// const blob = new Blob([file.fileData.data], { type: 'application/octet-stream' });
|
||||||
URL.revokeObjectURL(fileSrc);
|
// const blob = new Blob([file.fileData], { type: 'image/png'});
|
||||||
}, 1000);
|
// let fileSrc = file.fileData;
|
||||||
return fileSrc;
|
|
||||||
|
let localUrl: string = "";
|
||||||
|
if (file instanceof File) {
|
||||||
|
localUrl = URL.createObjectURL(file as Blob);
|
||||||
|
} else if (file.filePath) {
|
||||||
|
// const blob = new Blob([file.fileData]);
|
||||||
|
// localUrl = URL.createObjectURL(blob);
|
||||||
|
const parsed = JSON.parse(file.fileData);
|
||||||
|
// retrieve the original buffer of data
|
||||||
|
const buff = Buffer.from(parsed.blob, "base64");
|
||||||
|
const blob = new Blob([buff], { type: 'application/octet-stream' });
|
||||||
|
// file.blob = blob;
|
||||||
|
localUrl = URL.createObjectURL(blob);
|
||||||
|
file.fileSrc = localUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTimeout(() => {
|
||||||
|
// URL.revokeObjectURL(localUrl);
|
||||||
|
// }, 1000);
|
||||||
|
return localUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileSize(file) {
|
|
||||||
|
|
||||||
|
// private async downloadFile(id: number): Promise<string> {
|
||||||
|
// const response = await axios.get<Blob>(`/api/download/${id}`, {
|
||||||
|
// responseType: 'blob',
|
||||||
|
// });
|
||||||
|
// const url = URL.createObjectURL(response.data);
|
||||||
|
// setTimeout(() => {
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// }, 1000);
|
||||||
|
// return url;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public getFileSize(file) {
|
||||||
if (file.size > 1024) {
|
if (file.size > 1024) {
|
||||||
if (file.size > 1048576) {
|
if (file.size > 1048576) {
|
||||||
return Math.round(file.size / 1048576) + 'mb';
|
return Math.round(file.size / 1048576) + 'mb';
|
||||||
|
@ -370,29 +406,75 @@ export default class FileUploadComponent extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if file is of type image and prepend the initialied
|
// private _addFile(file) {
|
||||||
// template to the target element
|
// // const isImage = file.type.match('image.*');
|
||||||
private _addFile(file: TestFile) {
|
// // const objectURL = URL.createObjectURL(file);
|
||||||
// const isImage = file.type.match('image.*');
|
|
||||||
// const objectURL = URL.createObjectURL(file);
|
|
||||||
|
|
||||||
// this.files[objectURL] = file;
|
// // this.files[objectURL] = file;
|
||||||
// let test: TethysFile = { upload: file, label: "dfdsfs", sorting: 0 };
|
// // let test: TethysFile = { upload: file, label: "dfdsfs", sorting: 0 };
|
||||||
// file.sorting = this.files.length;
|
// // file.sorting = this.files.length;
|
||||||
this.files.push(file);
|
// file.sort_order = (this.items.length + 1),
|
||||||
|
// this.files.push(file);
|
||||||
|
// }
|
||||||
|
|
||||||
|
private _addFile(file: File) {
|
||||||
|
// const reader = new FileReader();
|
||||||
|
// reader.onload = (event) => {
|
||||||
|
// const base64Data = (event.target as FileReader).result as string;
|
||||||
|
// this.items.push(test);
|
||||||
|
|
||||||
|
// };
|
||||||
|
// reader.readAsDataURL(file);
|
||||||
|
if (file instanceof File) {
|
||||||
|
// const base64Data = await this.readBase64(file);
|
||||||
|
let test: TethysFile = {
|
||||||
|
label: file.name,
|
||||||
|
name: file.name,
|
||||||
|
size: file.size,
|
||||||
|
file_size: file.size,
|
||||||
|
// fileData: JSON.stringify({ blob: base64Data }),
|
||||||
|
// filePath: file.mozFullPath,
|
||||||
|
// path_name: file.mozFullPath,
|
||||||
|
webkitRelativePath: '',
|
||||||
|
lastModified: file.lastModified,
|
||||||
|
type: file.type,
|
||||||
|
mime_type: file.type,
|
||||||
|
visible_in_frontdoor: false,
|
||||||
|
visible_in_oai: false,
|
||||||
|
fileSrc: file.type.match('image.*')? this.generateURL(file) : "",
|
||||||
|
blob: file as Blob,
|
||||||
|
sort_order: (this.items.length + 1),
|
||||||
|
};
|
||||||
|
// this.items.push(test);
|
||||||
|
this.items[this.items.length] = test;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.items.push(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private async readBase64(blob: Blob): Promise<string> {
|
||||||
|
// return new Promise<string>((resolve, reject) => {
|
||||||
|
// const reader = new FileReader();
|
||||||
|
// reader.onload = (event) => resolve((event.target as FileReader).result as string);
|
||||||
|
// reader.onerror = reject;
|
||||||
|
// reader.readAsDataURL(blob);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
// use to check if a file is being dragged
|
// use to check if a file is being dragged
|
||||||
private _hasFiles({ types = [] as Array<string> }) {
|
private _hasFiles({ types = [] as Array<string> }) {
|
||||||
return types.indexOf('Files') > -1;
|
return types.indexOf('Files') > -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default FileUploadComponent;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.hasImage:hover section {
|
.hasImage:hover section {
|
||||||
background-color: rgba(5, 5, 5, 0.4);
|
background-color: rgba(5, 5, 5, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasImage:hover button:hover {
|
.hasImage:hover button:hover {
|
||||||
background: rgba(5, 5, 5, 0.45);
|
background: rgba(5, 5, 5, 0.45);
|
||||||
}
|
}
|
||||||
|
@ -409,6 +491,7 @@ i {
|
||||||
#overlay.draggedover {
|
#overlay.draggedover {
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#overlay.draggedover p,
|
#overlay.draggedover p,
|
||||||
#overlay.draggedover i {
|
#overlay.draggedover i {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import FormCheckRadio from '@/Components/FormCheckRadio.vue';
|
import FormCheckRadio from '@/Components/FormCheckRadio.vue';
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -13,7 +13,7 @@ const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'checkbox',
|
default: 'checkbox',
|
||||||
validator: (value) => ['checkbox', 'radio', 'switch'].includes(value),
|
validator: (value: string) => ['checkbox', 'radio', 'switch'].includes(value),
|
||||||
},
|
},
|
||||||
componentClass: {
|
componentClass: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -27,11 +27,34 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
const computedValue = computed({
|
const computedValue = computed({
|
||||||
get: () => props.modelValue,
|
// get: () => props.modelValue,
|
||||||
|
get: () => {
|
||||||
|
// const ids = props.modelValue.map((obj) => obj.id);
|
||||||
|
// return ids;
|
||||||
|
if (Array.isArray(props.modelValue)) {
|
||||||
|
if (props.modelValue.every((item) => typeof item === 'number')) {
|
||||||
|
return props.modelValue;
|
||||||
|
} else if (props.modelValue.every((item) => hasIdAttribute(item))) {
|
||||||
|
const ids = props.modelValue.map((obj) => obj.id.toString());
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
return props.modelValue;
|
||||||
|
}
|
||||||
|
// return props.modelValue;
|
||||||
|
},
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
emit('update:modelValue', value);
|
emit('update:modelValue', value);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Define a type guard to check if an object has an 'id' attribute
|
||||||
|
// function hasIdAttribute(obj: any): obj is { id: any } {
|
||||||
|
// return typeof obj === 'object' && 'id' in obj;
|
||||||
|
// }
|
||||||
|
|
||||||
|
const hasIdAttribute = (obj: any): obj is { id: any } => {
|
||||||
|
return typeof obj === 'object' && 'id' in obj;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -59,8 +59,9 @@ Map.include({
|
||||||
});
|
});
|
||||||
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
|
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
|
||||||
const DEFAULT_BASE_LAYER_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
|
const DEFAULT_BASE_LAYER_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
|
||||||
// const OPEN_SEARCH_HOST = 'http://localhost:9200';
|
// const OPENSEARCH_HOST = 'http://localhost:9200';
|
||||||
const OPEN_SEARCH_HOST = 'http://192.168.21.18';
|
const OPENSEARCH_HOST = 'http://192.168.21.18';
|
||||||
|
// const OPENSEARCH_HOST = `http://${process.env.OPENSEARCH_PUBLIC_HOST}`;
|
||||||
let map: Map;
|
let map: Map;
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -226,7 +227,7 @@ const handleDrawEventCreated = async (event) => {
|
||||||
try {
|
try {
|
||||||
let response = await axios({
|
let response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: OPEN_SEARCH_HOST + '/tethys-records/_search',
|
url: OPENSEARCH_HOST + '/tethys-records/_search',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
data: {
|
data: {
|
||||||
size: 1000,
|
size: 1000,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { gradientBgPurplePink, gradientBgDark, gradientBgPinkRed, gradientBgGree
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bg: {
|
bg: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: false,
|
||||||
validator: (value) => ['purplePink', 'pinkRed', 'greenBlue'].includes(value),
|
validator: (value) => ['purplePink', 'pinkRed', 'greenBlue'].includes(value),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -23,9 +23,11 @@ const colorClass = computed(() => {
|
||||||
return gradientBgPinkRed;
|
return gradientBgPinkRed;
|
||||||
case 'greenBlue':
|
case 'greenBlue':
|
||||||
return gradientBgGreenBlue;
|
return gradientBgGreenBlue;
|
||||||
|
default:
|
||||||
|
return 'bg-white text-black dark:bg-slate-900/70 dark:text-white';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'bg-white';
|
// return 'bg-white';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,10 @@ export interface Dataset {
|
||||||
| (IErrorMessage | undefined)
|
| (IErrorMessage | undefined)
|
||||||
| Coverage
|
| Coverage
|
||||||
| Array<DatasetReference>
|
| Array<DatasetReference>
|
||||||
| Array<File>;
|
| Array<File>
|
||||||
|
| (Array<number> | Array<Object>);
|
||||||
language: Ref<string>;
|
language: Ref<string>;
|
||||||
// licenses: Array<number>;
|
licenses: Array<number> | Array<Object>;
|
||||||
rights: boolean;
|
rights: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
creating_corporation: string;
|
creating_corporation: string;
|
||||||
|
@ -29,24 +30,49 @@ export interface Dataset {
|
||||||
// async (user): Promise<void>;
|
// async (user): Promise<void>;
|
||||||
subjects: Array<Subject>;
|
subjects: Array<Subject>;
|
||||||
references: Array<DatasetReference>;
|
references: Array<DatasetReference>;
|
||||||
files: Array<TestFile> | undefined;
|
files: Array<TethysFile>;
|
||||||
// upload: TethysFile
|
// upload: TethysFile
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides information about files and allows JavaScript in a web page to access their content. */
|
/** Provides information about files and allows JavaScript in a web page to access their content. */
|
||||||
export interface TestFile extends Blob {
|
// export interface TethysFile {
|
||||||
|
|
||||||
|
// readonly lastModified: number;
|
||||||
|
// readonly name: string;
|
||||||
|
// readonly webkitRelativePath: string;
|
||||||
|
// id: number;
|
||||||
|
// label: string;
|
||||||
|
// sorting: number;
|
||||||
|
// filePath: string;
|
||||||
|
// fileSrc: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface TethysFile {
|
||||||
readonly lastModified: number;
|
readonly lastModified: number;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly webkitRelativePath: string;
|
readonly webkitRelativePath: string;
|
||||||
|
id?: number;
|
||||||
label: string;
|
label: string;
|
||||||
sorting: number;
|
// sorting: number;
|
||||||
}
|
// path_name?: string; //only db path_name
|
||||||
|
filePath?: string;
|
||||||
|
fileSrc?: string;
|
||||||
|
blob: Blob;
|
||||||
|
fileData?: any;
|
||||||
|
|
||||||
// export interface TethysFile {
|
//additional:
|
||||||
// label: string,
|
comment?: string;
|
||||||
// sorting: number,
|
document_id?: number;
|
||||||
// upload: File,
|
file_size: number;
|
||||||
// }
|
language?: string;
|
||||||
|
mime_type: string;
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
size: number;
|
||||||
|
sort_order: number;
|
||||||
|
visible_in_frontdoor: boolean;
|
||||||
|
visible_in_oai: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Subject {
|
export interface Subject {
|
||||||
// id: number;
|
// id: number;
|
||||||
|
@ -64,12 +90,14 @@ export interface DatasetReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Title {
|
export interface Title {
|
||||||
|
id?: number;
|
||||||
value: string;
|
value: string;
|
||||||
type: string;
|
type: string;
|
||||||
language: string | Ref<string>;
|
language: string | Ref<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Description {
|
export interface Description {
|
||||||
|
id?: number;
|
||||||
value: string;
|
value: string;
|
||||||
type: string;
|
type: string;
|
||||||
language: string | Ref<string>;
|
language: string | Ref<string>;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import FormControl from '@/Components/FormControl.vue';
|
||||||
<LayoutGuest>
|
<LayoutGuest>
|
||||||
<Head title="Login" />
|
<Head title="Login" />
|
||||||
|
|
||||||
|
<!-- <SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'"> -->
|
||||||
<SectionFullScreen v-slot="{ cardClass }">
|
<SectionFullScreen v-slot="{ cardClass }">
|
||||||
<a class="text-2xl font-semibold flex justify-center items-center mb-8 lg:mb-10">
|
<a class="text-2xl font-semibold flex justify-center items-center mb-8 lg:mb-10">
|
||||||
<img src="/logo.svg" class="h-10 mr-4" alt="Windster Logo" />
|
<img src="/logo.svg" class="h-10 mr-4" alt="Windster Logo" />
|
||||||
|
|
|
@ -22,9 +22,7 @@ import CardBox from '@/Components/CardBox.vue';
|
||||||
import FormField from '@/Components/FormField.vue';
|
import FormField from '@/Components/FormField.vue';
|
||||||
import FormControl from '@/Components/FormControl.vue';
|
import FormControl from '@/Components/FormControl.vue';
|
||||||
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
|
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
|
||||||
// import BaseDivider from '@/Components/BaseDivider.vue';
|
|
||||||
import BaseButton from '@/Components/BaseButton.vue';
|
import BaseButton from '@/Components/BaseButton.vue';
|
||||||
// import BaseButtons from '@/Components/BaseButtons.vue';
|
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
// import { Inertia } from '@inertiajs/inertia';
|
// import { Inertia } from '@inertiajs/inertia';
|
||||||
import CardBoxModal from '@/Components/CardBoxModal.vue';
|
import CardBoxModal from '@/Components/CardBoxModal.vue';
|
||||||
|
@ -196,7 +194,7 @@ if (Object.keys(mainService.dataset).length == 0) {
|
||||||
// titles: [{ value: '', type: 'Main', language: language }],
|
// titles: [{ value: '', type: 'Main', language: language }],
|
||||||
// descriptions: [{ value: '', type: 'Abstract', language: language }],
|
// descriptions: [{ value: '', type: 'Abstract', language: language }],
|
||||||
// });
|
// });
|
||||||
let form = useForm<Dataset>(dataset);
|
let form = useForm<Dataset>(dataset as Dataset);
|
||||||
// form.defaults();
|
// form.defaults();
|
||||||
|
|
||||||
// const emit = defineEmits(['update:modelValue', 'setRef']);
|
// const emit = defineEmits(['update:modelValue', 'setRef']);
|
||||||
|
@ -292,11 +290,14 @@ const submit = async () => {
|
||||||
|
|
||||||
// this.currentStatus = STATUS_SAVING;
|
// this.currentStatus = STATUS_SAVING;
|
||||||
// serrors = [];
|
// serrors = [];
|
||||||
|
// const files = form.files.map((obj) => {
|
||||||
|
// return new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
|
||||||
|
// });
|
||||||
|
|
||||||
// formStep.value++;
|
// formStep.value++;
|
||||||
await form
|
await form
|
||||||
.transform((data) => ({
|
.transform((data) => ({
|
||||||
...data,
|
...data,
|
||||||
rights: form.rights && form.rights == true ? 'true' : 'false',
|
rights: form.rights && form.rights == true ? 'true' : 'false',
|
||||||
}))
|
}))
|
||||||
.post(route, {
|
.post(route, {
|
||||||
|
@ -729,8 +730,9 @@ Removes a selected keyword
|
||||||
</SearchAutocomplete>
|
</SearchAutocomplete>
|
||||||
|
|
||||||
<TablePersons :persons="form.contributors" v-if="form.contributors.length > 0"
|
<TablePersons :persons="form.contributors" v-if="form.contributors.length > 0"
|
||||||
:contributortypes="contributorTypes" :errors="form.errors"/>
|
:contributortypes="contributorTypes" :errors="form.errors" />
|
||||||
<div class="text-red-400 text-sm" v-if="form.errors.contributors && Array.isArray(form.errors.contributors)">
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors.contributors && Array.isArray(form.errors.contributors)">
|
||||||
{{ form.errors.contributors.join(', ') }}
|
{{ form.errors.contributors.join(', ') }}
|
||||||
</div>
|
</div>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
@ -1013,7 +1015,7 @@ Removes a selected keyword
|
||||||
</p>
|
</p>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<FileUploadComponent v-model="form.files"></FileUploadComponent>
|
<FileUploadComponent :files="form.files"></FileUploadComponent>
|
||||||
|
|
||||||
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['file'])">
|
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['file'])">
|
||||||
{{ form.errors['file'].join(', ') }}
|
{{ form.errors['file'].join(', ') }}
|
||||||
|
@ -1039,7 +1041,8 @@ Removes a selected keyword
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button v-if="formStep == 4" :disabled="form.processing" :class="{ 'opacity-25': form.processing }"
|
<button v-if="formStep == 4" :disabled="form.processing"
|
||||||
|
:class="{ 'opacity-25': form.processing }"
|
||||||
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
|
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
|
||||||
@click.stop="submit">
|
@click.stop="submit">
|
||||||
Save
|
Save
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<!-- <div class="max-w-2xl mx-auto"> -->
|
<!-- <div class="max-w-2xl mx-auto"> -->
|
||||||
|
|
||||||
<CardBox :form="true">
|
<CardBox :form="true">
|
||||||
|
<FormValidationErrors v-bind:errors="errors" />
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<!-- <label for="title" class="block text-gray-700 font-bold mb-2">Title:</label>
|
<!-- <label for="title" class="block text-gray-700 font-bold mb-2">Title:</label>
|
||||||
<input
|
<input
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
v-model="form.language"
|
v-model="form.language"
|
||||||
/> -->
|
/> -->
|
||||||
<div class="flex flex-col md:flex-row">
|
<div class="flex flex-col md:flex-row">
|
||||||
|
<!-- (1) language field -->
|
||||||
<FormField label="Language *" help="required: select dataset main language"
|
<FormField label="Language *" help="required: select dataset main language"
|
||||||
:class="{ 'text-red-400': errors.language }" class="w-full flex-1">
|
:class="{ 'text-red-400': errors.language }" class="w-full flex-1">
|
||||||
<FormControl required v-model="form.language" :type="'select'" placeholder="[Enter Language]"
|
<FormControl required v-model="form.language" :type="'select'" placeholder="[Enter Language]"
|
||||||
|
@ -31,17 +33,41 @@
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<FormField label="Dataset Type *" help="required: dataset type"
|
|
||||||
:class="{ 'text-red-400': form.errors.type }">
|
<!-- (2) licenses -->
|
||||||
<FormControl required v-model="form.type" :type="'select'" placeholder="-- select type --"
|
<FormField label="Licenses" wrap-body :class="{ 'text-red-400': form.errors.licenses }"
|
||||||
:errors="errors.type" :options="doctypes">
|
class="mt-8 w-full mx-2 flex-1">
|
||||||
<div class="text-red-400 text-sm" v-if="form.errors.type && Array.isArray(form.errors.type)">
|
<FormCheckRadioGroup v-model="form.licenses" name="licenses" is-column :options="licenses" />
|
||||||
{{ form.errors.type.join(', ') }}
|
|
||||||
</div>
|
|
||||||
</FormControl>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<!-- titles -->
|
<div class="flex flex-col md:flex-row">
|
||||||
|
<!-- (3) dataset_type -->
|
||||||
|
<FormField label="Dataset Type *" help="required: dataset type"
|
||||||
|
:class="{ 'text-red-400': form.errors.type }" class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.type" :type="'select'" placeholder="-- select type --"
|
||||||
|
:errors="errors.type" :options="doctypes">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors.type && Array.isArray(form.errors.type)">
|
||||||
|
{{ form.errors.type.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
<!-- (4) creating_corporation -->
|
||||||
|
<FormField label="Creating Corporation *"
|
||||||
|
:class="{ 'text-red-400': form.errors.creating_corporation }" class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.creating_corporation" type="text"
|
||||||
|
placeholder="[enter creating corporation]" :is-read-only="true">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors.creating_corporation && Array.isArray(form.errors.creating_corporation)">
|
||||||
|
{{ form.errors.creating_corporation.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseDivider />
|
||||||
|
|
||||||
|
<!-- (5) titles -->
|
||||||
<CardBox class="mb-6 shadow" :has-form-data="false" title="Titles" :icon="mdiFinance"
|
<CardBox class="mb-6 shadow" :has-form-data="false" title="Titles" :icon="mdiFinance"
|
||||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addTitle()">
|
:header-icon="mdiPlusCircle" v-on:header-icon-click="addTitle()">
|
||||||
<div class="flex flex-col md:flex-row">
|
<div class="flex flex-col md:flex-row">
|
||||||
|
@ -79,8 +105,8 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="(item, index) in form.titles" :key="index">
|
<template v-for="(title, index) in form.titles" :key="index">
|
||||||
<tr v-if="item.type != 'Main'">
|
<tr v-if="title.type != 'Main'">
|
||||||
<!-- <td scope="row">{{ index + 1 }}</td> -->
|
<!-- <td scope="row">{{ index + 1 }}</td> -->
|
||||||
<td data-label="Title Value">
|
<td data-label="Title Value">
|
||||||
<FormControl required v-model="form.titles[index].value" type="text"
|
<FormControl required v-model="form.titles[index].value" type="text"
|
||||||
|
@ -113,7 +139,7 @@
|
||||||
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
||||||
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
||||||
<BaseButton color="danger" :icon="mdiTrashCan" small
|
<BaseButton color="danger" :icon="mdiTrashCan" small
|
||||||
@click.prevent="removeTitle(index)" />
|
v-if="title.id == undefined" @click.prevent="removeTitle(index)" />
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -122,6 +148,7 @@
|
||||||
</table>
|
</table>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
||||||
|
<!-- (6) descriptions -->
|
||||||
<CardBox class="mb-6 shadow" :has-form-data="false" title="Descriptions" :icon="mdiFinance"
|
<CardBox class="mb-6 shadow" :has-form-data="false" title="Descriptions" :icon="mdiFinance"
|
||||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addDescription()">
|
:header-icon="mdiPlusCircle" v-on:header-icon-click="addDescription()">
|
||||||
<div class="flex flex-col md:flex-row">
|
<div class="flex flex-col md:flex-row">
|
||||||
|
@ -172,7 +199,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td data-label="Description Type">
|
<td data-label="Description Type">
|
||||||
<FormControl required v-model="form.descriptions[index].type" type="select"
|
<FormControl required v-model="form.descriptions[index].type" type="select"
|
||||||
:options="props.descriptiontypes" placeholder="[select title type]">
|
:options="descriptiontypes" placeholder="[select title type]">
|
||||||
<div class="text-red-400 text-sm"
|
<div class="text-red-400 text-sm"
|
||||||
v-if="Array.isArray(form.errors[`descriptions.${index}.type`])">
|
v-if="Array.isArray(form.errors[`descriptions.${index}.type`])">
|
||||||
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
|
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
|
||||||
|
@ -191,7 +218,7 @@
|
||||||
<td class="before:hidden lg:w-1 whitespace-nowrap">
|
<td class="before:hidden lg:w-1 whitespace-nowrap">
|
||||||
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
||||||
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
||||||
<BaseButton color="danger" :icon="mdiTrashCan" small
|
<BaseButton color="danger" :icon="mdiTrashCan" small v-if="item.id == undefined"
|
||||||
@click.prevent="removeDescription(index)" />
|
@click.prevent="removeDescription(index)" />
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</td>
|
</td>
|
||||||
|
@ -201,14 +228,182 @@
|
||||||
</table>
|
</table>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
||||||
<MapComponent v-if="form.coverage"
|
<!-- (7) authors -->
|
||||||
:mapOptions="mapOptions"
|
<CardBox class="mb-6 shadow" has-table title="Authors" :icon="mdiBookOpenPageVariant">
|
||||||
:baseMaps="baseMaps"
|
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
|
||||||
:fitBounds="fitBounds"
|
placeholder="search in person table...." v-on:person="onAddAuthor"></SearchAutocomplete>
|
||||||
:coverage="form.coverage"
|
|
||||||
:mapId="mapId"
|
<TablePersons :persons="form.authors" v-if="form.authors.length > 0" />
|
||||||
v-bind-event:onMapInitializedEvent="onMapInitialized"
|
<div class="text-red-400 text-sm" v-if="errors.authors && Array.isArray(errors.authors)">
|
||||||
></MapComponent>
|
{{ errors.authors.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- (8) contributors -->
|
||||||
|
<CardBox class="mb-6 shadow" has-table title="Contributors" :icon="mdiBookOpenPageVariant">
|
||||||
|
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
|
||||||
|
placeholder="search in person table...." v-on:person="onAddContributor">
|
||||||
|
</SearchAutocomplete>
|
||||||
|
|
||||||
|
<TablePersons :persons="form.contributors" v-if="form.contributors.length > 0"
|
||||||
|
:contributortypes="contributorTypes" :errors="form.errors" />
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors.contributors && Array.isArray(form.errors.contributors)">
|
||||||
|
{{ form.errors.contributors.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
|
||||||
|
<div class="flex flex-col md:flex-row">
|
||||||
|
<!-- (9) project_id -->
|
||||||
|
<FormField label="Project.." help="project is optional"
|
||||||
|
:class="{ 'text-red-400': errors.project_id }" class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.project_id" :type="'select'" placeholder="[Select Project]"
|
||||||
|
:errors="form.errors.project_id" :options="projects">
|
||||||
|
<div class="text-red-400 text-sm" v-if="form.errors.project_id">
|
||||||
|
{{ form.errors.project_id.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
<!-- (10) embargo_date -->
|
||||||
|
<FormField label="Embargo Date.." help="embargo date is optional"
|
||||||
|
:class="{ 'text-red-400': errors.embargo_date }" class="w-full mx-2 flex-1">
|
||||||
|
<FormControl v-model="form.embargo_date" :type="'date'" placeholder="date('y-m-d')"
|
||||||
|
:errors="form.errors.embargo_date">
|
||||||
|
<div class="text-red-400 text-sm" v-if="form.errors.embargo_date">
|
||||||
|
{{ form.errors.embargo_date.join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseDivider />
|
||||||
|
|
||||||
|
<MapComponent v-if="form.coverage" :mapOptions="mapOptions" :baseMaps="baseMaps" :fitBounds="fitBounds"
|
||||||
|
:coverage="form.coverage" :mapId="mapId" v-bind-event:onMapInitializedEvent="onMapInitialized">
|
||||||
|
</MapComponent>
|
||||||
|
<div class="flex flex-col md:flex-row">
|
||||||
|
<!-- x min and max -->
|
||||||
|
<FormField label="Coverage X Min" :class="{ 'text-red-400': form.errors['coverage.x_min'] }"
|
||||||
|
class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.coverage.x_min" type="text" placeholder="[enter x_min]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors['coverage.x_min'] && Array.isArray(form.errors['coverage.x_min'])">
|
||||||
|
{{ form.errors['coverage.x_min'].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Coverage X Max" :class="{ 'text-red-400': form.errors['coverage.x_max'] }"
|
||||||
|
class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.coverage.x_max" type="text" placeholder="[enter x_max]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors['coverage.x_max'] && Array.isArray(form.errors['coverage.x_max'])">
|
||||||
|
{{ form.errors['coverage.x_max'].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
<!-- y min and max -->
|
||||||
|
<FormField label="Coverage Y Min" :class="{ 'text-red-400': form.errors['coverage.y_min'] }"
|
||||||
|
class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.coverage.y_min" type="text" placeholder="[enter y_min]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors['coverage.y_min'] && Array.isArray(form.errors['coverage.y_min'])">
|
||||||
|
{{ form.errors['coverage.y_min'].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Coverage Y Max" :class="{ 'text-red-400': form.errors['coverage.y_max'] }"
|
||||||
|
class="w-full mx-2 flex-1">
|
||||||
|
<FormControl required v-model="form.coverage.y_max" type="text" placeholder="[enter y_max]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors['coverage.y_max'] && Array.isArray(form.errors['coverage.y_max'])">
|
||||||
|
{{ form.errors['coverage.y_max'].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CardBox class="mb-6 shadow" has-table title="Dataset References" :header-icon="mdiPlusCircle"
|
||||||
|
v-on:header-icon-click="addReference">
|
||||||
|
<table class="table-fixed border-green-900" v-if="form.references.length">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-4/12">Value</th>
|
||||||
|
<th class="w-2/12">Type</th>
|
||||||
|
<th class="w-3/12">Relation</th>
|
||||||
|
<th class="w-2/12">Label</th>
|
||||||
|
<th class="w-1/12"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, index) in form.references">
|
||||||
|
<td data-label="Reference Value">
|
||||||
|
<!-- <input name="Reference Value" class="form-control"
|
||||||
|
placeholder="[VALUE]" v-model="item.value" /> -->
|
||||||
|
<FormControl required v-model="item.value" :type="'text'" placeholder="[VALUE]"
|
||||||
|
:errors="form.errors.embargo_date">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors[`references.${index}.value`] && Array.isArray(form.errors[`references.${index}.value`])">
|
||||||
|
{{ form.errors[`references.${index}.value`].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<FormControl required v-model="form.references[index].type" type="select"
|
||||||
|
:options="referenceIdentifierTypes" placeholder="[type]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="Array.isArray(form.errors[`references.${index}.type`])">
|
||||||
|
{{ form.errors[`references.${index}.type`].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<!-- {!! Form::select('Reference[Relation]', $relationTypes, null,
|
||||||
|
['placeholder' => '[relationType]', 'v-model' => 'item.relation',
|
||||||
|
'data-vv-scope' => 'step-2'])
|
||||||
|
!!} -->
|
||||||
|
<FormControl required v-model="form.references[index].relation" type="select"
|
||||||
|
:options="relationTypes" placeholder="[relation type]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="Array.isArray(form.errors[`references.${index}.relation`])">
|
||||||
|
{{ form.errors[`references.${index}.relation`].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</td>
|
||||||
|
<td data-label="Reference Label">
|
||||||
|
<!-- <input name="Reference Label" class="form-control" v-model="item.label" /> -->
|
||||||
|
<FormControl required v-model="form.references[index].label" type="text"
|
||||||
|
placeholder="[reference label]">
|
||||||
|
<div class="text-red-400 text-sm"
|
||||||
|
v-if="form.errors[`references.${index}.label`] && Array.isArray(form.errors[`references.${index}.label`])">
|
||||||
|
{{ form.errors[`references.${index}.label`].join(', ') }}
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
</td>
|
||||||
|
<td class="before:hidden lg:w-1 whitespace-nowrap">
|
||||||
|
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
||||||
|
<BaseButton color="danger" :icon="mdiTrashCan" small
|
||||||
|
@click.prevent="removeReference(index)" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</CardBox>
|
||||||
|
|
||||||
|
<BaseDivider />
|
||||||
|
|
||||||
|
<CardBox class="mb-6 shadow" has-table title="Dataset Keywords" :icon="mdiEarthPlus"
|
||||||
|
:header-icon="mdiPlusCircle" v-on:header-icon-click="addKeyword">
|
||||||
|
<!-- <ul>
|
||||||
|
<li v-for="(subject, index) in form.subjects" :key="index">
|
||||||
|
{{ subject.value }} <BaseButton color="danger" :icon="mdiTrashCan" small @click.prevent="removeKeyword(index)" />
|
||||||
|
</li>
|
||||||
|
</ul> -->
|
||||||
|
<TableKeywords :keywords="form.subjects" :errors="form.errors" :subjectTypes="subjectTypes"
|
||||||
|
v-if="form.subjects.length > 0" />
|
||||||
|
</CardBox>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -233,18 +428,14 @@
|
||||||
</option>
|
</option>
|
||||||
</select> -->
|
</select> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
|
||||||
<label for="license" class="block text-gray-700 font-bold mb-2">License:</label>
|
<FileUploadComponent :files="form.files"></FileUploadComponent>
|
||||||
<!-- <select
|
|
||||||
id="license"
|
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['files'])">
|
||||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
{{ form.errors['files'].join(', ') }}
|
||||||
v-model="dataset.license_id"
|
|
||||||
>
|
|
||||||
<option v-for="license in licenses" :key="license.id" :value="license.id" class="block px-4 py-2 text-gray-700">
|
|
||||||
{{ license.name_long }}
|
|
||||||
</option>
|
|
||||||
</select> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Add more input fields for the other properties of the dataset -->
|
<!-- Add more input fields for the other properties of the dataset -->
|
||||||
<!-- <button
|
<!-- <button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -252,11 +443,17 @@
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button> -->
|
</button> -->
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<BaseButtons>
|
<BaseButtons>
|
||||||
<BaseButton type="submit" label="Submit" color="info" :class="{ 'opacity-25': form.processing }"
|
<BaseButton @click.stop="submit" :disabled="form.processing" label="Save" color="info"
|
||||||
:disabled="form.processing">
|
:class="{ 'opacity-25': form.processing }" small>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
<!-- <button :disabled="form.processing" :class="{ 'opacity-25': form.processing }"
|
||||||
|
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
|
||||||
|
@click.stop="submit">
|
||||||
|
Save
|
||||||
|
</button> -->
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
@ -266,31 +463,61 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, useForm } from '@inertiajs/vue3';
|
// import EditComponent from "./../EditComponent";
|
||||||
|
// export default EditComponent;
|
||||||
|
|
||||||
|
// import { Component, Vue, Prop, Setup, toNative } from 'vue-facing-decorator';
|
||||||
|
// import AuthLayout from '@/Layouts/Auth.vue';
|
||||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
import { Dataset, Title } from '@/Dataset';
|
import { useForm, Head } from '@inertiajs/vue3';
|
||||||
|
// import { ref } from 'vue';
|
||||||
|
// import { MainService } from '@/Stores/main';
|
||||||
|
// import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
|
||||||
|
import { Dataset, Title, Subject, TethysFile } from '@/Dataset';
|
||||||
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
|
|
||||||
import FormField from '@/Components/FormField.vue';
|
import FormField from '@/Components/FormField.vue';
|
||||||
import FormControl from '@/Components/FormControl.vue';
|
import FormControl from '@/Components/FormControl.vue';
|
||||||
import SectionMain from '@/Components/SectionMain.vue';
|
import SectionMain from '@/Components/SectionMain.vue';
|
||||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||||
import { mdiImageText, mdiArrowLeftBoldOutline, mdiPlusCircle, mdiFinance, mdiTrashCan } from '@mdi/js';
|
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
|
||||||
// import BaseDivider from '@/Components/BaseDivider.vue';
|
|
||||||
import BaseButton from '@/Components/BaseButton.vue';
|
import BaseButton from '@/Components/BaseButton.vue';
|
||||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||||
|
import BaseDivider from '@/Components/BaseDivider.vue';
|
||||||
import CardBox from '@/Components/CardBox.vue';
|
import CardBox from '@/Components/CardBox.vue';
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
|
||||||
|
|
||||||
import MapComponent from '@/Components/Map/map.component.vue';
|
import MapComponent from '@/Components/Map/map.component.vue';
|
||||||
|
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
|
||||||
|
import TablePersons from '@/Components/TablePersons.vue';
|
||||||
|
import TableKeywords from '@/Components/TableKeywords.vue';
|
||||||
|
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||||
|
import FileUploadComponent from '@/Components/FileUpload.vue';
|
||||||
import { MapOptions } from '@/Components/Map/MapOptions';
|
import { MapOptions } from '@/Components/Map/MapOptions';
|
||||||
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
||||||
import { LayerOptions } from '@/Components/Map/LayerOptions';
|
import { LayerOptions } from '@/Components/Map/LayerOptions';
|
||||||
|
import {
|
||||||
|
mdiImageText,
|
||||||
|
mdiArrowLeftBoldOutline,
|
||||||
|
mdiPlusCircle,
|
||||||
|
mdiFinance,
|
||||||
|
mdiTrashCan,
|
||||||
|
mdiBookOpenPageVariant,
|
||||||
|
mdiEarthPlus,
|
||||||
|
} from '@mdi/js';
|
||||||
|
import { notify } from '@/notiwind';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataset: {
|
errors: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
licenses: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
languages: {},
|
|
||||||
doctypes: {
|
doctypes: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
|
@ -299,14 +526,37 @@ const props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
projects: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
descriptiontypes: {
|
descriptiontypes: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
errors: {
|
contributorTypes: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
subjectTypes: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
referenceIdentifierTypes: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
relationTypes: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
dataset: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// const projects = reactive([]);
|
// const projects = reactive([]);
|
||||||
|
@ -325,7 +575,42 @@ const fitBounds: LatLngBoundsExpression = [
|
||||||
];
|
];
|
||||||
const mapId = 'test';
|
const mapId = 'test';
|
||||||
|
|
||||||
|
// const downloadFile = async (id: string): Promise<string> => {
|
||||||
|
// const response = await axios.get<Blob>(`/api/download/${id}`, {
|
||||||
|
// responseType: 'blob',
|
||||||
|
// });
|
||||||
|
// const url = URL.createObjectURL(response.data);
|
||||||
|
// setTimeout(() => {
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// }, 1000);
|
||||||
|
// return url;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// for (const file of props.dataset.files) {
|
||||||
|
// // console.log(`${file.name} path is ${file.filePath} here.`);
|
||||||
|
// file.fileSrc = ref("");
|
||||||
|
// // downloadFile(file.id).then((value: string) => {
|
||||||
|
// // file.fileSrc = ref(value);
|
||||||
|
// // form = useForm<Dataset>(props.dataset as Dataset);
|
||||||
|
// // });
|
||||||
|
// }
|
||||||
|
|
||||||
let form = useForm<Dataset>(props.dataset as Dataset);
|
let form = useForm<Dataset>(props.dataset as Dataset);
|
||||||
|
|
||||||
|
// const mainService = MainService();
|
||||||
|
// mainService.fetchfiles(props.dataset);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const files = computed(() => props.dataset.file);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// let form = useForm<Dataset>(props.dataset as Dataset);
|
||||||
|
|
||||||
// const form = useForm({
|
// const form = useForm({
|
||||||
// _method: 'put',
|
// _method: 'put',
|
||||||
// login: props.user.login,
|
// login: props.user.login,
|
||||||
|
@ -342,9 +627,63 @@ let form = useForm<Dataset>(props.dataset as Dataset);
|
||||||
// this.projects = data.projects;
|
// this.projects = data.projects;
|
||||||
// this.licenses = data.licenses;
|
// this.licenses = data.licenses;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const submit = async (): Promise<void> => {
|
||||||
|
let route = stardust.route('dataset.update', [props.dataset.id]);
|
||||||
|
// await Inertia.post('/app/register', this.form);
|
||||||
|
// await router.post('/app/register', this.form);
|
||||||
|
|
||||||
|
if (form.licenses.every((item) => hasIdAttribute(item))) {
|
||||||
|
form.licenses = form.licenses.map((obj) => obj.id.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const [fileUploads, fileInputs] = form.files?.reduce(
|
||||||
|
([fileUploads, fileInputs], obj) => {
|
||||||
|
if (!obj.id) {
|
||||||
|
// return MultipartFile for file upload
|
||||||
|
fileUploads[obj.sort_order] = new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
|
||||||
|
} else {
|
||||||
|
// return normal request input
|
||||||
|
fileInputs.push(obj);
|
||||||
|
}
|
||||||
|
return [fileUploads, fileInputs];
|
||||||
|
},
|
||||||
|
[[], []] as [Array<File>, Array<TethysFile>]
|
||||||
|
) as [Array<File>, Array<TethysFile>];
|
||||||
|
|
||||||
|
await form
|
||||||
|
.transform((data) => ({
|
||||||
|
...data,
|
||||||
|
licenses: form.licenses.every((item) => hasIdAttribute(item))
|
||||||
|
? form.licenses.map((obj) => obj.id.toString())
|
||||||
|
: form.licenses,
|
||||||
|
files: fileUploads,
|
||||||
|
fileInputs: fileInputs,
|
||||||
|
// files: form.files.map((obj) => {
|
||||||
|
// let file;
|
||||||
|
// if (!obj.id) {
|
||||||
|
// // return MultipartFile for file upload
|
||||||
|
// file = new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
|
||||||
|
// } else {
|
||||||
|
// // return normal request input
|
||||||
|
// file = obj;
|
||||||
|
// }
|
||||||
|
// return file;
|
||||||
|
// }),
|
||||||
|
|
||||||
|
rights: 'true',
|
||||||
|
}))
|
||||||
|
.put(route);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasIdAttribute = (obj: any): obj is { id: any } => {
|
||||||
|
return typeof obj === 'object' && 'id' in obj;
|
||||||
|
};
|
||||||
|
|
||||||
const addTitle = () => {
|
const addTitle = () => {
|
||||||
let newTitle: Title = { value: '', language: '', type: '' };
|
let newTitle: Title = { value: '', language: '', type: '' };
|
||||||
//this.dataset.files.push(uploadedFiles[i]);
|
|
||||||
form.titles.push(newTitle);
|
form.titles.push(newTitle);
|
||||||
};
|
};
|
||||||
const removeTitle = (key) => {
|
const removeTitle = (key) => {
|
||||||
|
@ -353,20 +692,58 @@ const removeTitle = (key) => {
|
||||||
|
|
||||||
const addDescription = () => {
|
const addDescription = () => {
|
||||||
let newDescription = { value: '', language: '', type: '' };
|
let newDescription = { value: '', language: '', type: '' };
|
||||||
//this.dataset.files.push(uploadedFiles[i]);
|
|
||||||
form.descriptions.push(newDescription);
|
form.descriptions.push(newDescription);
|
||||||
};
|
};
|
||||||
const removeDescription = (key) => {
|
const removeDescription = (key) => {
|
||||||
form.descriptions.splice(key, 1);
|
form.descriptions.splice(key, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAddAuthor = (person) => {
|
||||||
|
if (form.authors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
|
||||||
|
} else if (form.contributors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' });
|
||||||
|
} else {
|
||||||
|
form.authors.push(person);
|
||||||
|
notify({ type: 'info', text: 'person has been successfully added as author' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddContributor = (person) => {
|
||||||
|
if (form.contributors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' }, 4000);
|
||||||
|
} else if (form.authors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
|
||||||
|
} else {
|
||||||
|
// person.pivot = { contributor_type: '' };
|
||||||
|
// // person.pivot = { name_type: '', contributor_type: '' };
|
||||||
|
form.contributors.push(person);
|
||||||
|
notify({ type: 'info', text: 'person has been successfully added as contributor' }, 4000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addKeyword = () => {
|
||||||
|
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
|
||||||
|
//this.dataset.files.push(uploadedFiles[i]);
|
||||||
|
form.subjects.push(newSubject);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addReference = () => {
|
||||||
|
let newReference = { value: '', label: '', relation: '', type: '' };
|
||||||
|
//this.dataset.files.push(uploadedFiles[i]);
|
||||||
|
form.references.push(newReference);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeReference = (key) => {
|
||||||
|
form.references.splice(key, 1);
|
||||||
|
};
|
||||||
|
|
||||||
const onMapInitialized = (newItem) => {
|
const onMapInitialized = (newItem) => {
|
||||||
// notify({ type: 'info', text: message });
|
|
||||||
console.log(newItem);
|
console.log(newItem);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- <style>
|
<style>
|
||||||
.max-w-2xl {
|
.max-w-2xl {
|
||||||
max-width: 2xl;
|
max-width: 2xl;
|
||||||
}
|
}
|
||||||
|
@ -398,4 +775,4 @@ const onMapInitialized = (newItem) => {
|
||||||
0 2px 4px 0 rgba(66, 72, 78, 0.12),
|
0 2px 4px 0 rgba(66, 72, 78, 0.12),
|
||||||
0 4px 8px 0 rgba(66, 72, 78, 0.16);
|
0 4px 8px 0 rgba(66, 72, 78, 0.16);
|
||||||
}
|
}
|
||||||
</style> -->
|
</style>
|
||||||
|
|
240
resources/js/Pages/Submitter/EditComponent.ts
Normal file
240
resources/js/Pages/Submitter/EditComponent.ts
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
import { Component, Vue, Prop, toNative } from 'vue-facing-decorator';
|
||||||
|
// import AuthLayout from '@/Layouts/Auth.vue';
|
||||||
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
|
import { useForm, InertiaForm, Head } from '@inertiajs/vue3';
|
||||||
|
import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
|
||||||
|
import { Dataset, Title, Subject } from '@/Dataset';
|
||||||
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
|
|
||||||
|
import FormField from '@/Components/FormField.vue';
|
||||||
|
import FormControl from '@/Components/FormControl.vue';
|
||||||
|
import SectionMain from '@/Components/SectionMain.vue';
|
||||||
|
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||||
|
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
|
||||||
|
import BaseButton from '@/Components/BaseButton.vue';
|
||||||
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||||
|
import BaseDivider from '@/Components/BaseDivider.vue';
|
||||||
|
import CardBox from '@/Components/CardBox.vue';
|
||||||
|
import MapComponent from '@/Components/Map/map.component.vue';
|
||||||
|
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
|
||||||
|
import TablePersons from '@/Components/TablePersons.vue';
|
||||||
|
import TableKeywords from '@/Components/TableKeywords.vue';
|
||||||
|
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||||
|
import FileUploadComponent from '@/Components/FileUpload.vue';
|
||||||
|
import { MapOptions } from '@/Components/Map/MapOptions';
|
||||||
|
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
||||||
|
import { LayerOptions } from '@/Components/Map/LayerOptions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mdiImageText,
|
||||||
|
mdiArrowLeftBoldOutline,
|
||||||
|
mdiPlusCircle,
|
||||||
|
mdiFinance,
|
||||||
|
mdiTrashCan,
|
||||||
|
mdiBookOpenPageVariant,
|
||||||
|
mdiEarthPlus,
|
||||||
|
} from '@mdi/js';
|
||||||
|
import { notify } from '@/notiwind';
|
||||||
|
|
||||||
|
export interface IErrorMessage {
|
||||||
|
[key: string]: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
name: 'EditComponent',
|
||||||
|
components: {
|
||||||
|
LayoutAuthenticated,
|
||||||
|
FormInput,
|
||||||
|
Head,
|
||||||
|
FormField,
|
||||||
|
FormControl,
|
||||||
|
SectionMain,
|
||||||
|
SectionTitleLineWithButton,
|
||||||
|
FormCheckRadioGroup,
|
||||||
|
BaseButton,
|
||||||
|
BaseButtons,
|
||||||
|
BaseDivider,
|
||||||
|
CardBox,
|
||||||
|
MapComponent,
|
||||||
|
SearchAutocomplete,
|
||||||
|
TablePersons,
|
||||||
|
TableKeywords,
|
||||||
|
FormValidationErrors,
|
||||||
|
FileUploadComponent,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
class EditComponent extends Vue {
|
||||||
|
// Component Property
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public errors: IErrorMessage;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public licenses;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public languages;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public doctypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public titletypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public projects;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public descriptiontypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => {} })
|
||||||
|
public contributorTypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public subjectTypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public referenceIdentifierTypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public relationTypes;
|
||||||
|
|
||||||
|
@Prop({ type: Object, default: () => ({}) })
|
||||||
|
public dataset: Dataset;
|
||||||
|
|
||||||
|
// @Prop({
|
||||||
|
// type: Object,
|
||||||
|
// default: () => ({}),
|
||||||
|
// })
|
||||||
|
// public datasetHasLicenses;
|
||||||
|
|
||||||
|
// Data Property
|
||||||
|
// public form: InertiaForm<Dataset>; // = useForm<Dataset>(this.dataset as Dataset);
|
||||||
|
|
||||||
|
// public form : InertiaForm<Dataset>= useForm<Dataset>([]);
|
||||||
|
|
||||||
|
// @Setup(() => useForm<Dataset>(this.dataset as Dataset))
|
||||||
|
public form: InertiaForm<Dataset>;
|
||||||
|
|
||||||
|
// @Hook
|
||||||
|
created() {
|
||||||
|
this.form = useForm<Dataset>(this.dataset as Dataset);
|
||||||
|
// this.form.licenses = this.datasetHasLicenses;
|
||||||
|
this.form.uploads = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapOptions: MapOptions = {
|
||||||
|
center: [48.208174, 16.373819],
|
||||||
|
zoom: 3,
|
||||||
|
zoomControl: false,
|
||||||
|
attributionControl: false,
|
||||||
|
};
|
||||||
|
public baseMaps: Map<string, LayerOptions> = new Map<string, LayerOptions>();
|
||||||
|
public fitBounds: LatLngBoundsExpression = [
|
||||||
|
[46.4318173285, 9.47996951665],
|
||||||
|
[49.0390742051, 16.9796667823],
|
||||||
|
];
|
||||||
|
public mapId = 'test';
|
||||||
|
mdiImageText = mdiImageText;
|
||||||
|
mdiArrowLeftBoldOutline = mdiArrowLeftBoldOutline;
|
||||||
|
mdiPlusCircle = mdiPlusCircle;
|
||||||
|
mdiFinance = mdiFinance;
|
||||||
|
mdiTrashCan = mdiTrashCan;
|
||||||
|
mdiBookOpenPageVariant = mdiBookOpenPageVariant;
|
||||||
|
mdiEarthPlus = mdiEarthPlus;
|
||||||
|
stardust = stardust;
|
||||||
|
|
||||||
|
// mounted() {
|
||||||
|
// this.form = useForm<Dataset>(this.dataset as Dataset);// Initialize myData with the value of propValue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public results: Array<any> = [];
|
||||||
|
|
||||||
|
// Component method
|
||||||
|
public async submit(): Promise<void> {
|
||||||
|
let route = this.stardust.route('dataset.update', [this.dataset.id]);
|
||||||
|
// await Inertia.post('/app/register', this.form);
|
||||||
|
// await router.post('/app/register', this.form);
|
||||||
|
|
||||||
|
if (this.form.licenses.every((item) => this.hasIdAttribute(item))) {
|
||||||
|
this.form.licenses = this.form.licenses.map((obj) => obj.id.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.form
|
||||||
|
.transform((data) => ({
|
||||||
|
...data,
|
||||||
|
licenses: this.form.licenses.every((item) => this.hasIdAttribute(item))
|
||||||
|
? this.form.licenses.map((obj) => obj.id.toString())
|
||||||
|
: this.form.licenses,
|
||||||
|
rights: 'true',
|
||||||
|
}))
|
||||||
|
.put(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasIdAttribute(obj: any): obj is { id: any } {
|
||||||
|
return typeof obj === 'object' && 'id' in obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addTitle(): void {
|
||||||
|
const newTitle: Title = { value: '', language: '', type: '' };
|
||||||
|
this.form.titles.push(newTitle);
|
||||||
|
}
|
||||||
|
public removeTitle(key: number): void {
|
||||||
|
this.form.titles.splice(key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addDescription(): void {
|
||||||
|
const newDescription = { value: '', language: '', type: '' };
|
||||||
|
this.form.descriptions.push(newDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeDescription(key: number): void {
|
||||||
|
this.form.descriptions.splice(key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddAuthor(person) {
|
||||||
|
if (this.form.authors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
|
||||||
|
} else if (this.form.contributors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' });
|
||||||
|
} else {
|
||||||
|
this.form.authors.push(person);
|
||||||
|
notify({ type: 'info', text: 'person has been successfully added as author' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddContributor(person) {
|
||||||
|
if (this.form.contributors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' }, 4000);
|
||||||
|
} else if (this.form.authors.filter((e) => e.id === person.id).length > 0) {
|
||||||
|
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
|
||||||
|
} else {
|
||||||
|
// person.pivot = { contributor_type: '' };
|
||||||
|
// // person.pivot = { name_type: '', contributor_type: '' };
|
||||||
|
this.form.contributors.push(person);
|
||||||
|
notify({ type: 'info', text: 'person has been successfully added as contributor' }, 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public addKeyword() {
|
||||||
|
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
|
||||||
|
//this.dataset.files.push(uploadedFiles[i]);
|
||||||
|
this.form.subjects.push(newSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addReference() {
|
||||||
|
let newReference = { value: '', label: '', relation: '', type: '' };
|
||||||
|
//this.dataset.files.push(uploadedFiles[i]);
|
||||||
|
this.form.references.push(newReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeReference(key) {
|
||||||
|
this.form.references.splice(key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onMapInitialized(newItem: any): void {
|
||||||
|
console.log(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default toNative(EditComponent);
|
||||||
|
// export default toNative(EditComponent);
|
|
@ -39,6 +39,7 @@ export const MainService = defineStore('main', {
|
||||||
authors: [] as Array<Person>,
|
authors: [] as Array<Person>,
|
||||||
// persons: [] as Array<Person>,
|
// persons: [] as Array<Person>,
|
||||||
datasets: [],
|
datasets: [],
|
||||||
|
files:[],
|
||||||
|
|
||||||
dataset: {} as Dataset,
|
dataset: {} as Dataset,
|
||||||
}),
|
}),
|
||||||
|
@ -108,5 +109,19 @@ export const MainService = defineStore('main', {
|
||||||
alert(error.message);
|
alert(error.message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// fetchfiles(id) {
|
||||||
|
// // sampleDataKey= authors or datasets
|
||||||
|
// axios
|
||||||
|
// .get(`api/files/${id}`)
|
||||||
|
// .then((r) => {
|
||||||
|
// if (r.data) {
|
||||||
|
// this[sampleDataKey] = r.data;
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((error) => {
|
||||||
|
// alert(error.message);
|
||||||
|
// });
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -172,6 +172,12 @@ Route.group(() => {
|
||||||
.as('dataset.edit')
|
.as('dataset.edit')
|
||||||
.where('id', Route.matchers.number())
|
.where('id', Route.matchers.number())
|
||||||
.middleware(['auth', 'can:dataset-submit']);
|
.middleware(['auth', 'can:dataset-submit']);
|
||||||
|
|
||||||
|
Route.put('/dataset/:id/update', 'DatasetController.update')
|
||||||
|
.as('dataset.update')
|
||||||
|
.where('id', Route.matchers.number())
|
||||||
|
.middleware(['auth', 'can:dataset-submit']);
|
||||||
|
|
||||||
Route.get('/dataset/:id/delete', 'DatasetController.delete').as('dataset.delete').middleware(['auth', 'can:dataset-delete']);
|
Route.get('/dataset/:id/delete', 'DatasetController.delete').as('dataset.delete').middleware(['auth', 'can:dataset-delete']);
|
||||||
Route.put('/dataset/:id/deleteupdate', 'DatasetController.deleteUpdate')
|
Route.put('/dataset/:id/deleteupdate', 'DatasetController.deleteUpdate')
|
||||||
.as('dataset.deleteUpdate')
|
.as('dataset.deleteUpdate')
|
||||||
|
|
|
@ -16,6 +16,8 @@ Route.group(() => {
|
||||||
Route.get('/dataset/:publish_id', 'DatasetController.findOne').as('dataset.findOne');
|
Route.get('/dataset/:publish_id', 'DatasetController.findOne').as('dataset.findOne');
|
||||||
Route.get('/sitelinks/:year', 'HomeController.findDocumentsPerYear');
|
Route.get('/sitelinks/:year', 'HomeController.findDocumentsPerYear');
|
||||||
Route.get('/years', 'HomeController.findYears');
|
Route.get('/years', 'HomeController.findYears');
|
||||||
|
|
||||||
|
Route.get('/download/:id', 'FileController.findOne').as('file.findOne');
|
||||||
});
|
});
|
||||||
// .middleware("auth:api");
|
// .middleware("auth:api");
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
const { join, resolve, dirname } = require('path');
|
const { join, resolve, dirname } = require('path');
|
||||||
const Encore = require('@symfony/webpack-encore');
|
const Encore = require('@symfony/webpack-encore');
|
||||||
const { VueLoaderPlugin } = require('vue-loader');
|
const { VueLoaderPlugin } = require('vue-loader');
|
||||||
|
const dotenv = require('dotenv-webpack');
|
||||||
|
|
||||||
|
// Load the environment variables from the.env file
|
||||||
|
Encore.addPlugin(
|
||||||
|
new dotenv({
|
||||||
|
path: ".env",
|
||||||
|
defaults: ".env",
|
||||||
|
systemvars: true,
|
||||||
|
allowEmptyValues: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const babelLoader = {
|
const babelLoader = {
|
||||||
// test: /\.js$/,
|
// test: /\.js$/,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user