- second commit

This commit is contained in:
Kaimbacher 2023-03-17 16:13:37 +01:00
parent 4fc3bb0a01
commit 59a99ff3c8
61 changed files with 2625 additions and 1182 deletions

View File

@ -8,7 +8,7 @@ README.md
# Docker stuff # Docker stuff
.dockerignore .dockerignore
Dockerfile* Dockerfile*
docker-compose.yml

11
.prettierrc Normal file
View File

@ -0,0 +1,11 @@
{
"trailingComma": "all",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"useTabs": false,
"quoteProps": "consistent",
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 140
}

View File

@ -45,7 +45,7 @@ FROM base AS production
# Copy package.* to the working directory with active user # Copy package.* to the working directory with active user
COPY --chown=node:node ./package*.json ./ COPY --chown=node:node ./package*.json ./
# We run NPM CI to install the exact versions of dependencies # We run NPM CI to install the exact versions of production dependencies
RUN npm ci --omit=dev RUN npm ci --omit=dev
# Copy files to the working directory from the build folder the user # Copy files to the working directory from the build folder the user
COPY --chown=node:node --from=build /home/node/app/build . COPY --chown=node:node --from=build /home/node/app/build .

View File

@ -117,7 +117,7 @@ export default class UsersController {
const roles = await Role.query().pluck('name', 'id'); const roles = await Role.query().pluck('name', 'id');
// const userHasRoles = user.roles; // const userHasRoles = user.roles;
const userHasRoles = await user.related('roles').query().orderBy('name').pluck('id'); const userHasRoles = await user.related('roles').query().orderBy('name').pluck('id');
// let test = Object.keys(userHasRoles).map((key) => userHasRoles[key]);
return inertia.render('Admin/User/Edit', { return inertia.render('Admin/User/Edit', {
roles: roles, roles: roles,
user: user, user: user,

View File

@ -4,20 +4,34 @@ import Person from 'App/Models/Person';
// node ace make:controller Author // node ace make:controller Author
export default class AuthorsController { export default class AuthorsController {
public async index({}: HttpContextContract) {
// select * from gba.persons
// where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id"
// where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id"));
const authors = await Person.query()
.whereHas('datasets', (dQuery) => {
dQuery.wherePivot('role', 'author');
})
.withCount('datasets', (query) => {
query.as('datasets_count');
});
public async index({}: HttpContextContract) { return authors;
// select * from gba.persons }
// where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id"
// where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id"));
const authors = await Person.query()
.whereHas('datasets', (dQuery) => {
dQuery.wherePivot('role', 'author')
})
.withCount('datasets', (query) => {
query.as('datasets_count')
});
return authors;
}
public async persons({ request }: HttpContextContract) {
const authors = Person.query().where('status', true);
if (request.input('filter')) {
// users = users.whereRaw('name like %?%', [request.input('search')])
const searchTerm = request.input('filter');
authors
.where('first_name', 'ilike', `%${searchTerm}%`)
.orWhere('last_name', 'like', `%${searchTerm}%`)
.orWhere('email', 'like', `%${searchTerm}%`);
}
let persons = await authors;
return persons;
}
} }

View File

@ -0,0 +1,162 @@
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
// import User from 'App/Models/User';
// import Role from 'App/Models/Role';
// import Database from '@ioc:Adonis/Lucid/Database';
import License from 'App/Models/License';
// import type { ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm';
// import CreateUserValidator from 'App/Validators/CreateUserValidator';
// import UpdateUserValidator from 'App/Validators/UpdateUserValidator';
// import { RenderResponse } from '@ioc:EidelLev/Inertia';
import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator';
enum TitleTypes {
Main = 'Main',
Sub = 'Sub',
Alternative = 'Alternative',
Translated = 'Translated',
Other = 'Other',
}
enum DescriptionTypes {
Abstract = 'Abstract',
Methods = 'Methods',
Series_information = 'Series_information',
Technical_info = 'Technical_info',
Translated = 'Translated',
Other = 'Other',
}
export default class DatasetController {
public async create({ inertia }: HttpContextContract) {
const licenses = await License.query().select('id', 'name_long').pluck('name_long', 'id');
const doctypes = {
analysisdata: { label: 'Analysis', value: 'analysisdata' },
measurementdata: { label: 'Measurements', value: 'measurementdata' },
monitoring: 'Monitoring',
remotesensing: 'Remote Sensing',
gis: 'GIS',
models: 'Models',
mixedtype: 'Mixed Type',
};
// const titletypes = {
// Sub: 'Sub',
// Alternative: 'Alternative',
// Translated: 'Translated',
// Other: 'Other',
// };
// const languages = await Database.from('languages').select('*').where('active', true);
return inertia.render('Submitter/Dataset/Create', {
licenses: licenses,
doctypes: doctypes,
titletypes: Object.values(TitleTypes).filter((x) => x.valueOf() !== 'Main'),
descriptiontypes: Object.values(DescriptionTypes).filter((x) => x.valueOf() !== 'Abstract'),
// descriptiontypes: DescriptionTypes
});
}
public async firstStep({ request, response }: HttpContextContract) {
const newDatasetSchema = schema.create({
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')]),
});
try {
// Step 2 - Validate request body against the schema
await request.validate({ schema: newDatasetSchema, messages: this.messages });
// console.log({ payload });
} catch (error) {
// Step 3 - Handle errors
// return response.badRequest(error.messages);
throw error;
}
return response.redirect().back();
}
public async secondStep({ request, response }: HttpContextContract) {
const newDatasetSchema = schema.create({
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
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)]),
}),
),
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)]),
}),
),
});
try {
// Step 2 - Validate request body against the schema
await request.validate({ schema: newDatasetSchema, messages: this.messages });
// console.log({ payload });
} catch (error) {
// Step 3 - Handle errors
// return response.badRequest(error.messages);
throw error;
}
return response.redirect().back();
}
// public async store({ request, response, session }: HttpContextContract) {
// // node ace make:validator CreateUser
// try {
// // Step 2 - Validate request body against the schema
// await request.validate(CreateUserValidator);
// // console.log({ payload });
// } catch (error) {
// // Step 3 - Handle errors
// // return response.badRequest(error.messages);
// throw error;
// }
// const input = request.only(['login', 'email', 'password']);
// const user = await User.create(input);
// if (request.input('roles')) {
// const roles: Array<number> = request.input('roles');
// await user.related('roles').attach(roles);
// }
// session.flash('message', 'User has been created successfully');
// return response.redirect().toRoute('user.index');
// }
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',
'licences.minLength': 'at least {{ options.minLength }} permission must be defined',
'licences.*.number': 'Define roles 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',
'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',
};
}

37
app/Models/License.ts Normal file
View File

@ -0,0 +1,37 @@
import { column, BaseModel, SnakeCaseNamingStrategy } from '@ioc:Adonis/Lucid/Orm';
export default class License extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy();
public static primaryKey = 'id';
public static table = 'document_licences';
public static selfAssignPrimaryKey = false;
@column({
isPrimary: true,
})
public id: number;
@column({})
public active: boolean;
@column({})
public langauge: string;
@column({})
public link_licence: string;
@column({})
public link_logo: string;
@column({})
public display_name: string;
@column({})
public name_long: string;
@column({})
public name: string;
@column({})
public sortOrder: number;
}

View File

@ -54,7 +54,7 @@ export default class Person extends BaseModel {
serializeAs: 'name' serializeAs: 'name'
}) })
public get fullName() { public get fullName() {
return this.firstName + ' ' + this.lastName; return `${this.firstName} ${this.lastName} (${this.email})`;
} }
@computed() @computed()

View File

@ -3,101 +3,101 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
// import { Request } from '@adonisjs/core/build/standalone'; // import { Request } from '@adonisjs/core/build/standalone';
export default class UpdateUserValidator { export default class UpdateUserValidator {
protected ctx: HttpContextContract; protected ctx: HttpContextContract;
public schema; public schema;
constructor(ctx: HttpContextContract) { constructor(ctx: HttpContextContract) {
this.ctx = ctx; this.ctx = ctx;
this.schema = this.createSchema(); this.schema = this.createSchema();
} }
// public get schema() { // public get schema() {
// return this._schema; // return this._schema;
// } // }
private createSchema() { private createSchema() {
return schema.create({ return schema.create({
login: schema.string({ trim: true }, [ login: schema.string({ trim: true }, [
rules.minLength(3), rules.minLength(3),
rules.maxLength(50), rules.maxLength(50),
rules.unique({ rules.unique({
table: 'accounts', table: 'accounts',
column: 'login', column: 'login',
// whereNot: { id: this.refs.id } // whereNot: { id: this.refs.id }
whereNot: { id: this.ctx?.params.id }, whereNot: { id: this.ctx?.params.id },
}), }),
// rules.regex(/^[a-zA-Z0-9-_]+$/), // rules.regex(/^[a-zA-Z0-9-_]+$/),
//Must be alphanumeric with hyphens or underscores //Must be alphanumeric with hyphens or underscores
]), ]),
email: schema.string({}, [ email: schema.string({}, [
rules.email(), rules.email(),
rules.unique({ table: 'accounts', column: 'email', whereNot: { id: this.ctx?.params.id } }), rules.unique({ table: 'accounts', column: 'email', whereNot: { id: this.ctx?.params.id } }),
]), ]),
password: schema.string.optional([rules.confirmed(), rules.minLength(6)]), password: schema.string.optional([rules.confirmed(), rules.minLength(6)]),
roles: schema.array.optional([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user roles: schema.array.optional([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user
}); });
} }
/* /*
* Define schema to validate the "shape", "type", "formatting" and "integrity" of data. * Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
* *
* For example: * For example:
* 1. The username must be of data type string. But then also, it should * 1. The username must be of data type string. But then also, it should
* not contain special characters or numbers. * not contain special characters or numbers.
* ``` * ```
* schema.string({}, [ rules.alpha() ]) * schema.string({}, [ rules.alpha() ])
* ``` * ```
* *
* 2. The email must be of data type string, formatted as a valid * 2. The email must be of data type string, formatted as a valid
* email. But also, not used by any other user. * email. But also, not used by any other user.
* ``` * ```
* schema.string({}, [ * schema.string({}, [
* rules.email(), * rules.email(),
* rules.unique({ table: 'users', column: 'email' }), * rules.unique({ table: 'users', column: 'email' }),
* ]) * ])
* ``` * ```
*/ */
// public refs = schema.refs({ // public refs = schema.refs({
// id: this.ctx.params.id // id: this.ctx.params.id
// }) // })
// public schema = schema.create({ // public schema = schema.create({
// login: schema.string({ trim: true }, [ // login: schema.string({ trim: true }, [
// rules.minLength(3), // rules.minLength(3),
// rules.maxLength(50), // rules.maxLength(50),
// rules.unique({ // rules.unique({
// table: 'accounts', // table: 'accounts',
// column: 'login', // column: 'login',
// // whereNot: { id: this.refs.id } // // whereNot: { id: this.refs.id }
// whereNot: { id: this.ctx?.params.id }, // whereNot: { id: this.ctx?.params.id },
// }), // }),
// // rules.regex(/^[a-zA-Z0-9-_]+$/), // // rules.regex(/^[a-zA-Z0-9-_]+$/),
// //Must be alphanumeric with hyphens or underscores // //Must be alphanumeric with hyphens or underscores
// ]), // ]),
// email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]), // email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]),
// password: schema.string.optional([rules.confirmed(), rules.minLength(6)]), // password: schema.string.optional([rules.confirmed(), rules.minLength(6)]),
// roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user // roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user
// }); // });
/** /**
* Custom messages for validation failures. You can make use of dot notation `(.)` * Custom messages for validation failures. You can make use of dot notation `(.)`
* for targeting nested fields and array expressions `(*)` for targeting all * for targeting nested fields and array expressions `(*)` for targeting all
* children of an array. For example: * children of an array. For example:
* *
* { * {
* 'profile.username.required': 'Username is required', * 'profile.username.required': 'Username is required',
* 'scores.*.number': 'Define scores as valid numbers' * 'scores.*.number': 'Define scores as valid numbers'
* } * }
* *
*/ */
public messages: CustomMessages = { public messages: CustomMessages = {
'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long',
'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long',
'required': '{{ field }} is required', 'required': '{{ field }} is required',
'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',
'roles.minLength': 'at least {{ options.minLength }} role must be defined', 'roles.minLength': 'at least {{ options.minLength }} role must be defined',
'roles.*.number': 'Define roles as valid numbers', 'roles.*.number': 'Define roles as valid numbers',
}; };
} }

View File

@ -1,6 +1,8 @@
import { BaseCommand } from '@adonisjs/core/build/standalone'; import { BaseCommand } from '@adonisjs/core/build/standalone';
import crypto from 'crypto'; import crypto from 'crypto';
import fs from 'fs'; import fs from 'fs';
// import Config from '@ioc:Adonis/Core/Config';
import Logger from '@ioc:Adonis/Core/Logger'
export default class ValidateChecksum extends BaseCommand { export default class ValidateChecksum extends BaseCommand {
/** /**
@ -37,6 +39,9 @@ export default class ValidateChecksum extends BaseCommand {
// query all files from database: // query all files from database:
const files = await File.query().preload('hashvalues'); const files = await File.query().preload('hashvalues');
// const logLevel = Config.get('app.logger.level', 'info');
// console.log(this.logger.)
for (var file of files) { for (var file of files) {
let hashValue = await file.related('hashvalues').query().pluck('value', 'type'); let hashValue = await file.related('hashvalues').query().pluck('value', 'type');
@ -45,16 +50,17 @@ export default class ValidateChecksum extends BaseCommand {
try { try {
calculatedMd5FileHash = await this.checksumFile(filePath, 'md5'); calculatedMd5FileHash = await this.checksumFile(filePath, 'md5');
} catch (exception) { } catch (exception) {
this.logger.error(exception.message); // this.logger.error(exception.message);
Logger.error(exception.message);
continue; continue;
} }
if (hashValue['md5'] == calculatedMd5FileHash) { if (hashValue['md5'] == calculatedMd5FileHash) {
this.logger.info( Logger.info(
`File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`, `File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`,
); );
} else { } else {
this.logger.logError( Logger.error(
`File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`, `File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`,
); );
} }

View File

@ -154,6 +154,10 @@ export const logger: LoggerConfig = {
| |
*/ */
level: Env.get('LOG_LEVEL', 'info'), level: Env.get('LOG_LEVEL', 'info'),
redact: {
paths: ['password', '*.password'],
},
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -1,25 +1,25 @@
import BaseSchema from '@ioc:Adonis/Lucid/Schema' import BaseSchema from '@ioc:Adonis/Lucid/Schema';
import Config from '@ioc:Adonis/Core/Config' import Config from '@ioc:Adonis/Core/Config';
export default class Roles extends BaseSchema { export default class Roles extends BaseSchema {
protected tableName = Config.get('rolePermission.role_table', 'roles') protected tableName = Config.get('rolePermission.role_table', 'roles');
public async up () { public async up() {
this.schema.createTable(this.tableName, (table) => { this.schema.createTable(this.tableName, (table) => {
table.increments('id') table.increments('id');
table.string('name', 191).unique() table.string('name', 191).unique();
table.string('slug', 191).nullable().unique() table.string('slug', 191).nullable().unique();
table.string('description', 191).nullable() table.string('description', 191).nullable();
/** /**
* Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
*/ */
table.timestamp('created_at', { useTz: true }).nullable() table.timestamp('created_at', { useTz: true }).nullable();
table.timestamp('updated_at', { useTz: true }).nullable() table.timestamp('updated_at', { useTz: true }).nullable();
}) });
} }
public async down () { public async down() {
this.schema.dropTable(this.tableName) this.schema.dropTable(this.tableName);
} }
} }

32
env.ts
View File

@ -12,21 +12,21 @@
| |
*/ */
import Env from '@ioc:Adonis/Core/Env' import Env from '@ioc:Adonis/Core/Env';
export default Env.rules({ export default Env.rules({
HOST: Env.schema.string({ format: 'host' }), HOST: Env.schema.string({ format: 'host' }),
PORT: Env.schema.number(), PORT: Env.schema.number(),
APP_KEY: Env.schema.string(), APP_KEY: Env.schema.string(),
APP_NAME: Env.schema.string(), APP_NAME: Env.schema.string(),
CACHE_VIEWS: Env.schema.boolean(), CACHE_VIEWS: Env.schema.boolean(),
SESSION_DRIVER: Env.schema.string(), SESSION_DRIVER: Env.schema.string(),
DRIVE_DISK: Env.schema.enum(['local'] as const), DRIVE_DISK: Env.schema.enum(['local'] as const),
NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const), NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
DB_CONNECTION: Env.schema.string(), DB_CONNECTION: Env.schema.string(),
PG_HOST: Env.schema.string({ format: 'host' }), PG_HOST: Env.schema.string({ format: 'host' }),
PG_PORT: Env.schema.number(), PG_PORT: Env.schema.number(),
PG_USER: Env.schema.string(), PG_USER: Env.schema.string(),
PG_PASSWORD: Env.schema.string.optional(), PG_PASSWORD: Env.schema.string.optional(),
PG_DB_NAME: Env.schema.string(), PG_DB_NAME: Env.schema.string(),
}) });

625
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,17 +28,6 @@
"eslintIgnore": [ "eslintIgnore": [
"build" "build"
], ],
"prettier": {
"trailingComma": "all",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"useTabs": false,
"quoteProps": "consistent",
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 120
},
"alias": { "alias": {
"vue": "./node_modules/vue/dist/vue.esm-bundler.js" "vue": "./node_modules/vue/dist/vue.esm-bundler.js"
}, },
@ -71,7 +60,7 @@
"naive-ui": "^2.34.3", "naive-ui": "^2.34.3",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"pinia": "^2.0.30", "pinia": "^2.0.30",
"pino-pretty": "^9.1.1", "pino-pretty": "^10.0.0",
"postcss-loader": "^7.0.2", "postcss-loader": "^7.0.2",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",
@ -91,7 +80,9 @@
"@adonisjs/shield": "^7.1.0", "@adonisjs/shield": "^7.1.0",
"@adonisjs/view": "^6.1.5", "@adonisjs/view": "^6.1.5",
"@eidellev/adonis-stardust": "^3.0.0", "@eidellev/adonis-stardust": "^3.0.0",
"@eidellev/inertia-adonisjs": "^7.4.0", "@eidellev/inertia-adonisjs": "^8.0.0",
"@fontsource/archivo-black": "^4.5.9",
"@fontsource/inter": "^4.5.15",
"@inertiajs/inertia": "^0.11.1", "@inertiajs/inertia": "^0.11.1",
"@inertiajs/vue3": "^1.0.0", "@inertiajs/vue3": "^1.0.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",

View File

@ -1,4 +1,15 @@
{ {
"assets/app.css": "http://localhost:8080/assets/app.css", "assets/app.css": "http://localhost:8080/assets/app.css",
"assets/app.js": "http://localhost:8080/assets/app.js" "assets/app.js": "http://localhost:8080/assets/app.js",
"assets/fonts/inter-all-400-normal.woff": "http://localhost:8080/assets/fonts/inter-all-400-normal.8c804432.woff",
"assets/fonts/archivo-black-all-400-normal.woff": "http://localhost:8080/assets/fonts/archivo-black-all-400-normal.da68e413.woff",
"assets/fonts/inter-latin-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-latin-ext-400-normal.3a7a7652.woff2",
"assets/fonts/archivo-black-latin-400-normal.woff2": "http://localhost:8080/assets/fonts/archivo-black-latin-400-normal.fc847a1f.woff2",
"assets/fonts/inter-latin-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-latin-400-normal.be7cb18d.woff2",
"assets/fonts/archivo-black-latin-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/archivo-black-latin-ext-400-normal.21761451.woff2",
"assets/fonts/inter-cyrillic-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-cyrillic-ext-400-normal.fcc125c4.woff2",
"assets/fonts/inter-greek-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-greek-400-normal.0278a49f.woff2",
"assets/fonts/inter-cyrillic-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-cyrillic-400-normal.8684fef6.woff2",
"assets/fonts/inter-greek-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-greek-ext-400-normal.3f642a92.woff2",
"assets/fonts/inter-vietnamese-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-vietnamese-400-normal.789afb71.woff2"
} }

View File

@ -1,30 +1,44 @@
/* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap'); */ /* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap'); */
@import url('https://fonts.googleapis.com/css?family=Roboto:400,400i,600,700'); /* @import url('https://fonts.googleapis.com/css?family=Roboto:400,400i,600,700'); */
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@import "_checkbox-radio-switch.css"; @import '_checkbox-radio-switch.css';
@import "_progress.css"; @import '_progress.css';
@import "_scrollbars.css"; @import '_scrollbars.css';
@import "_table.css"; @import '_table.css';
@import '@fontsource/inter/index.css';
@import '@fontsource/archivo-black/index.css';
html, body { /* @layer base {
background-color: #F7F8FA; html,
font-family: 'Roboto', sans-serif; body {
/* height: 100vh; */ background-color: #f7f8fa;
color: #46444c; font-family: Inter, system-ui, sans-serif;
position: relative; height: 100vh;
} color: #46444c;
position: relative;
}
} */
/* html,
body {
background-color: #f7f8fa;
font-family: 'Roboto', sans-serif;
height: 100vh;
color: #46444c;
position: relative;
} */
.px-6 { .px-6 {
padding-left: 0; padding-left: 0;
/* padding-right: 1.5rem; */ /* padding-right: 1.5rem; */
} }
.rounded-md { .rounded-md {
color: gray; color: gray;
} }
/* body:before { /* body:before {
@ -90,6 +104,6 @@ main li:before {
} */ } */
main code { main code {
font-size: 16px; font-size: 16px;
background: #e6e2ff; background: #e6e2ff;
} }

View File

@ -2,8 +2,8 @@
// import { reactive, computed } from 'vue'; // import { reactive, computed } from 'vue';
// import { usePage } from '@inertiajs/vue3' // import { usePage } from '@inertiajs/vue3'
// import { usePage } from '@inertiajs/inertia-vue3'; // import { usePage } from '@inertiajs/inertia-vue3';
import { LayoutService } from '@/Stores/layout.js'; import { LayoutService } from '@/Stores/layout';
import menu from '@/menu.js' import menu from '@/menu'
import AsideMenuLayer from '@/Components/AsideMenuLayer.vue'; import AsideMenuLayer from '@/Components/AsideMenuLayer.vue';
import OverlayLayer from '@/Components/OverlayLayer.vue'; import OverlayLayer from '@/Components/OverlayLayer.vue';

View File

@ -3,7 +3,7 @@ import { ref, computed } from 'vue';
import { Link } from '@inertiajs/vue3'; import { Link } from '@inertiajs/vue3';
// import { Link } from '@inertiajs/inertia-vue3'; // import { Link } from '@inertiajs/inertia-vue3';
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
import { mdiMinus, mdiPlus } from '@mdi/js'; import { mdiMinus, mdiPlus } from '@mdi/js';
import { getButtonColor } from '@/colors.js'; import { getButtonColor } from '@/colors.js';
import BaseIcon from '@/Components/BaseIcon.vue'; import BaseIcon from '@/Components/BaseIcon.vue';

View File

@ -4,8 +4,8 @@ import { router } from '@inertiajs/vue3'
import { stardust } from '@eidellev/adonis-stardust/client'; import { stardust } from '@eidellev/adonis-stardust/client';
import { mdiLogout, mdiClose } from '@mdi/js'; import { mdiLogout, mdiClose } from '@mdi/js';
import { computed } from 'vue'; import { computed } from 'vue';
import { LayoutService } from '@/Stores/layout.js'; import { LayoutService } from '@/Stores/layout';
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
import AsideMenuList from '@/Components/AsideMenuList.vue'; import AsideMenuList from '@/Components/AsideMenuList.vue';
import AsideMenuItem from '@/Components/AsideMenuItem.vue'; import AsideMenuItem from '@/Components/AsideMenuItem.vue';
import BaseIcon from '@/Components/BaseIcon.vue'; import BaseIcon from '@/Components/BaseIcon.vue';

View File

@ -2,28 +2,28 @@
import AsideMenuItem from '@/Components/AsideMenuItem.vue'; import AsideMenuItem from '@/Components/AsideMenuItem.vue';
defineProps({ defineProps({
isDropdownList: Boolean, isDropdownList: Boolean,
menu: { menu: {
type: Object, type: Object,
default: () => {} default: () => {},
} },
}) });
const emit = defineEmits(['menu-click']) const emit = defineEmits(['menu-click']);
const menuClick = (event, item) => { const menuClick = (event, item) => {
emit('menu-click', event, item) emit('menu-click', event, item);
} };
</script> </script>
<template> <template>
<ul> <ul>
<AsideMenuItem <AsideMenuItem
v-for="(item, index) in menu" v-for="(item, index) in menu"
:key="index" :key="index"
v-bind:item="item" v-bind:item="item"
:is-dropdown-list="isDropdownList" :is-dropdown-list="isDropdownList"
@menu-click="menuClick" @menu-click="menuClick"
/> />
</ul> </ul>
</template> </template>

View File

@ -1,111 +1,81 @@
<script setup> <script setup>
import { mdiCog } from '@mdi/js' import { mdiCog } from '@mdi/js';
import { computed, useSlots } from 'vue' import { computed, useSlots } from 'vue';
import BaseIcon from '@/Components/BaseIcon.vue' import BaseIcon from '@/Components/BaseIcon.vue';
const props = defineProps({ const props = defineProps({
title: { title: {
type: String, type: String,
default: null default: null,
}, },
icon: { icon: {
type: String, type: String,
default: null default: null,
}, },
headerIcon: { headerIcon: {
type: String, type: String,
default: null default: null,
}, },
rounded: { rounded: {
type: String, type: String,
default: 'rounded-xl' default: 'rounded-xl',
}, },
hasTable: Boolean, hasFormData: Boolean,
empty: Boolean, empty: Boolean,
form: Boolean, form: Boolean,
hoverable: Boolean, hoverable: Boolean,
modal: Boolean modal: Boolean,
}) });
const emit = defineEmits(['header-icon-click', 'submit']) const emit = defineEmits(['header-icon-click', 'submit']);
const is = computed(() => props.form ? 'form' : 'div') const is = computed(() => (props.form ? 'form' : 'div'));
const slots = useSlots() const slots = useSlots();
const footer = computed(() => slots.footer && !!slots.footer()) const footer = computed(() => slots.footer && !!slots.footer());
const componentClass = computed(() => { const componentClass = computed(() => {
const base = [ const base = [props.rounded, props.modal ? 'dark:bg-slate-900' : 'dark:bg-slate-900/70'];
props.rounded,
props.modal ? 'dark:bg-slate-900' : 'dark:bg-slate-900/70'
]
if (props.hoverable) { if (props.hoverable) {
base.push('hover:shadow-lg transition-shadow duration-500') base.push('hover:shadow-lg transition-shadow duration-500');
} }
return base return base;
}) });
const computedHeaderIcon = computed(() => props.headerIcon ?? mdiCog) const computedHeaderIcon = computed(() => props.headerIcon ?? mdiCog);
const headerIconClick = () => { const headerIconClick = () => {
emit('header-icon-click') emit('header-icon-click');
} };
const submit = e => { const submit = (e) => {
emit('submit', e) emit('submit', e);
} };
</script> </script>
<template> <template>
<component <component :is="is" :class="componentClass" class="bg-white flex flex-col" @submit="submit">
:is="is" <header v-if="title" class="flex items-stretch border-b border-gray-100 dark:border-slate-800">
:class="componentClass" <div class="flex items-center py-3 grow font-bold" :class="[icon ? 'px-4' : 'px-6']">
class="bg-white flex flex-col" <BaseIcon v-if="icon" :path="icon" class="mr-3" />
@submit="submit" {{ title }}
> </div>
<header <button class="flex items-center py-3 px-4 justify-center ring-blue-700 focus:ring" @click="headerIconClick">
v-if="title" <BaseIcon :path="computedHeaderIcon" />
class="flex items-stretch border-b border-gray-100 dark:border-slate-800" </button>
> </header>
<div <div v-if="empty" class="text-center py-24 text-gray-500 dark:text-slate-400">
class="flex items-center py-3 grow font-bold" <p>Nothing's here</p>
:class="[ icon ? 'px-4' : 'px-6' ]" </div>
> <!-- <div v-else class="flex-1" :class="{'p-6':!hasTable}"> -->
<BaseIcon <div v-else class="flex-1" :class="[!hasFormData && 'p-6']">
v-if="icon" <slot />
:path="icon" </div>
class="mr-3" <div v-if="footer" class="p-6 border-t border-gray-100 dark:border-slate-800">
/> <slot name="footer" />
{{ title }} </div>
</div> </component>
<button
class="flex items-center py-3 px-4 justify-center ring-blue-700 focus:ring"
@click="headerIconClick"
>
<BaseIcon :path="computedHeaderIcon" />
</button>
</header>
<div
v-if="empty"
class="text-center py-24 text-gray-500 dark:text-slate-400"
>
<p>Nothing's here</p>
</div>
<div
v-else
class="flex-1"
:class="{'p-6':!hasTable}"
>
<slot />
</div>
<div
v-if="footer"
class="p-6 border-t border-gray-100 dark:border-slate-800"
>
<slot name="footer" />
</div>
</component>
</template> </template>

View File

@ -1,56 +1,62 @@
<script setup> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue';
import { mdiClose } from '@mdi/js' import { mdiClose } from '@mdi/js';
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 CardBox from '@/Components/CardBox.vue' import CardBox from '@/Components/CardBox.vue';
import OverlayLayer from '@/Components/OverlayLayer.vue' import OverlayLayer from '@/Components/OverlayLayer.vue';
const props = defineProps({ const props = defineProps({
title: { title: {
type: String, type: String,
default: null default: null,
}, },
largeTitle: { largeTitle: {
type: String, type: String,
default: null default: null,
}, },
button: { button: {
type: String, type: String,
default: 'info' default: 'info',
}, },
buttonLabel: { buttonLabel: {
type: String, type: String,
default: 'Done' default: 'Done',
}, },
hasCancel: Boolean, hasCancel: Boolean,
modelValue: { modelValue: {
type: [String, Number, Boolean], type: [String, Number, Boolean],
default: null default: null,
} },
}) });
const emit = defineEmits(['update:modelValue', 'cancel', 'confirm']) const emit = defineEmits(['update:modelValue', 'cancel', 'confirm']);
const value = computed({ const value = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: value => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value),
}) });
const confirmCancel = (mode) => { const confirmCancel = (mode) => {
value.value = false; value.value = false;
emit(mode); emit(mode);
} };
const confirm = () => confirmCancel('confirm') const confirm = () => confirmCancel('confirm');
const cancel = () => confirmCancel('cancel') const cancel = () => confirmCancel('cancel');
</script> </script>
<template> <template>
<OverlayLayer v-show="value" @overlay-click="cancel"> <OverlayLayer v-show="value" @overlay-click="cancel">
<CardBox v-show="value" :title="title" class="shadow-lg max-h-modal w-11/12 md:w-3/5 lg:w-2/5 xl:w-4/12 z-50" <CardBox
:header-icon="mdiClose" modal @header-icon-click="cancel"> v-show="value"
:title="title"
class="shadow-lg max-h-modal w-11/12 md:w-3/5 lg:w-2/5 xl:w-4/12 z-50"
:header-icon="mdiClose"
modal
@header-icon-click="cancel"
>
<div class="space-y-3"> <div class="space-y-3">
<h1 v-if="largeTitle" class="text-2xl"> <h1 v-if="largeTitle" class="text-2xl">
{{ largeTitle }} {{ largeTitle }}

View File

@ -3,66 +3,71 @@ import { computed, ref, onMounted, onBeforeUnmount } from 'vue';
import { MainService } from '@/Stores/main'; import { MainService } from '@/Stores/main';
import FormControlIcon from '@/Components/FormControlIcon.vue'; import FormControlIcon from '@/Components/FormControlIcon.vue';
const props = defineProps({ const props = defineProps({
name: { name: {
type: String, type: String,
default: null, default: null,
}, },
id: { id: {
type: String, type: String,
default: null, default: null,
}, },
autocomplete: { autocomplete: {
type: String, type: String,
default: null, default: null,
}, },
placeholder: { placeholder: {
type: String, type: String,
default: null, default: null,
}, },
inputmode: { inputmode: {
type: String, type: String,
default: null, default: null,
}, },
icon: { icon: {
type: String, type: String,
default: null, default: null,
}, },
options: { options: {
type: Array, type: [Array, Object],
default: null, default: null,
}, },
type: { type: {
type: String, type: String,
default: 'text', default: 'text',
}, },
modelValue: { isReadOnly: {
type: [String, Number, Boolean, Array, Object], type: Boolean,
default: '', default: false,
}, },
required: Boolean, modelValue: {
borderless: Boolean, type: [String, Number, Boolean, Array, Object],
transparent: Boolean, default: '',
ctrlKFocus: Boolean, },
required: Boolean,
borderless: Boolean,
transparent: Boolean,
ctrlKFocus: Boolean,
}); });
const emit = defineEmits(['update:modelValue', 'setRef']); const emit = defineEmits(['update:modelValue', 'setRef']);
const computedValue = computed({ const computedValue = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: (value) => { set: (value) => {
emit('update:modelValue', value); emit('update:modelValue', value);
}, },
}); });
const inputElClass = computed(() => { const inputElClass = computed(() => {
const base = [ const base = [
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full', 'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
'dark:placeholder-gray-400', 'dark:placeholder-gray-400',
computedType.value === 'textarea' ? 'h-24' : 'h-12', computedType.value === 'textarea' ? 'h-24' : 'h-12',
props.borderless ? 'border-0' : 'border', props.borderless ? 'border-0' : 'border',
props.transparent ? 'bg-transparent' : 'bg-white dark:bg-slate-800', // props.transparent && !props.isReadOnly ? 'bg-transparent' : 'bg-white dark:bg-slate-800',
]; props.isReadOnly ? 'bg-gray-50 dark:bg-slate-600' : 'bg-white dark:bg-slate-800',
if (props.icon) { ];
base.push('pl-10'); if (props.icon) {
} base.push('pl-10');
return base; }
return base;
}); });
const computedType = computed(() => (props.options ? 'select' : props.type)); const computedType = computed(() => (props.options ? 'select' : props.type));
const controlIconH = computed(() => (props.type === 'textarea' ? 'h-full' : 'h-12')); const controlIconH = computed(() => (props.type === 'textarea' ? 'h-full' : 'h-12'));
@ -71,74 +76,70 @@ const selectEl = ref(null);
const textareaEl = ref(null); const textareaEl = ref(null);
const inputEl = ref(null); const inputEl = ref(null);
onMounted(() => { onMounted(() => {
if (selectEl.value) { if (selectEl.value) {
emit('setRef', selectEl.value); emit('setRef', selectEl.value);
} else if (textareaEl.value) { } else if (textareaEl.value) {
emit('setRef', textareaEl.value); emit('setRef', textareaEl.value);
} else { } else {
emit('setRef', inputEl.value); emit('setRef', inputEl.value);
} }
}); });
if (props.ctrlKFocus) { if (props.ctrlKFocus) {
const fieldFocusHook = (e) => { const fieldFocusHook = (e) => {
if (e.ctrlKey && e.key === 'k') { if (e.ctrlKey && e.key === 'k') {
e.preventDefault(); e.preventDefault();
inputEl.value.focus(); inputEl.value.focus();
} else if (e.key === 'Escape') { } else if (e.key === 'Escape') {
inputEl.value.blur(); inputEl.value.blur();
} }
}; };
onMounted(() => { onMounted(() => {
if (!mainService.isFieldFocusRegistered) { if (!mainService.isFieldFocusRegistered) {
window.addEventListener('keydown', fieldFocusHook); window.addEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = true; mainService.isFieldFocusRegistered = true;
} else { } else {
// console.error('Duplicate field focus event') // console.error('Duplicate field focus event')
} }
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('keydown', fieldFocusHook); window.removeEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = false; mainService.isFieldFocusRegistered = false;
}); });
} }
</script> </script>
<template> <template>
<div class="relative"> <div class="relative">
<select <select v-if="computedType === 'select'" :id="id" v-model="computedValue" :name="name" :class="inputElClass">
v-if="computedType === 'select'" <option v-if="placeholder" class="text-opacity-25" value="" disabled selected>{{ placeholder }}</option>
:id="id" <option v-for="option in options" :key="option.id ?? option" :value="option.value ?? option">
v-model="computedValue" {{ option.label ?? option }}
:name="name" </option>
:class="inputElClass" </select>
> <textarea
<option v-for="option in options" :key="option.id ?? option" :value="option"> v-else-if="computedType === 'textarea'"
{{ option.label ?? option }} :id="id"
</option> v-model="computedValue"
</select> :class="inputElClass"
<textarea :name="name"
v-else-if="computedType === 'textarea'" :placeholder="placeholder"
:id="id" :required="required"
v-model="computedValue" />
:class="inputElClass" <input
:name="name" v-else
:placeholder="placeholder" :id="id"
:required="required" ref="inputEl"
/> v-model="computedValue"
<input :name="name"
v-else :inputmode="inputmode"
:id="id" :autocomplete="autocomplete"
ref="inputEl" :required="required"
v-model="computedValue" :placeholder="placeholder"
:name="name" :type="computedType"
:inputmode="inputmode" :class="inputElClass"
:autocomplete="autocomplete" :readonly="isReadOnly"
:required="required" />
:placeholder="placeholder" <FormControlIcon v-if="icon" :icon="icon" :h="controlIconH" />
:type="computedType" <slot />
:class="inputElClass" </div>
/>
<FormControlIcon v-if="icon" :icon="icon" :h="controlIconH" />
<slot />
</div>
</template> </template>

View File

@ -2,46 +2,47 @@
import { computed, useSlots } from 'vue'; import { computed, useSlots } from 'vue';
defineProps({ defineProps({
label: { label: {
type: String, type: String,
default: null, default: null,
}, },
labelFor: { labelFor: {
type: String, type: String,
default: null, default: null,
}, },
help: { help: {
type: String, type: String,
default: null, default: null,
}, },
}); });
const slots = useSlots(); const slots = useSlots();
const wrapperClass = computed(() => { const wrapperClass = computed(() => {
const base = []; const base = [];
const slotsLength = slots.default().length; const slotsLength = slots.default().length;
if (slotsLength > 1) { if (slotsLength > 1) {
base.push('grid grid-cols-1 gap-3'); base.push('grid grid-cols-1 gap-3');
} }
if (slotsLength === 2) { if (slotsLength === 2) {
base.push('md:grid-cols-2'); base.push('md:grid-cols-2');
} }
return base; return base;
}); });
</script> </script>
<template> <template>
<div class="mb-6 last:mb-0"> <div class="mb-6 last:mb-0">
<label v-if="label" :for="labelFor" class="block font-bold mb-2">{{ label }}</label> <!-- <label v-if="label" :for="labelFor" class="block font-bold mb-2">{{ label }}</label> -->
<div v-bind:class="wrapperClass"> <label v-if="label" :for="labelFor" class="font-bold h-6 mt-3 text-xs leading-8 uppercase">{{ label }}</label>
<slot /> <div v-bind:class="wrapperClass">
</div> <slot />
<div v-if="help" class="text-xs text-gray-500 dark:text-slate-400 mt-1"> </div>
{{ help }} <div v-if="help" class="text-xs text-gray-500 dark:text-slate-400 mt-1">
</div> {{ help }}
</div> </div>
</div>
</template> </template>

View File

@ -0,0 +1,44 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="my-svg-component"
>
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
</svg>
</template>
<script lang="ts">
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View File

@ -0,0 +1,35 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="my-svg-component">
<path
fill-rule="evenodd"
d="M9 2.25a.75.75 0 01.75.75v1.506a49.38 49.38 0 015.343.371.75.75 0 11-.186 1.489c-.66-.083-1.323-.151-1.99-.206a18.67 18.67 0 01-2.969 6.323c.317.384.65.753.998 1.107a.75.75 0 11-1.07 1.052A18.902 18.902 0 019 13.687a18.823 18.823 0 01-5.656 4.482.75.75 0 11-.688-1.333 17.323 17.323 0 005.396-4.353A18.72 18.72 0 015.89 8.598a.75.75 0 011.388-.568A17.21 17.21 0 009 11.224a17.17 17.17 0 002.391-5.165 48.038 48.038 0 00-8.298.307.75.75 0 01-.186-1.489 49.159 49.159 0 015.343-.371V3A.75.75 0 019 2.25zM15.75 9a.75.75 0 01.68.433l5.25 11.25a.75.75 0 01-1.36.634l-1.198-2.567h-6.744l-1.198 2.567a.75.75 0 01-1.36-.634l5.25-11.25A.75.75 0 0115.75 9zm-2.672 8.25h5.344l-2.672-5.726-2.672 5.726z"
clip-rule="evenodd"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View File

@ -0,0 +1,35 @@
<template>
<svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View File

@ -0,0 +1,33 @@
<template>
<svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M6 3a3 3 0 00-3 3v2.25a3 3 0 003 3h2.25a3 3 0 003-3V6a3 3 0 00-3-3H6zM15.75 3a3 3 0 00-3 3v2.25a3 3 0 003 3H18a3 3 0 003-3V6a3 3 0 00-3-3h-2.25zM6 12.75a3 3 0 00-3 3V18a3 3 0 003 3h2.25a3 3 0 003-3v-2.25a3 3 0 00-3-3H6zM17.625 13.5a.75.75 0 00-1.5 0v2.625H13.5a.75.75 0 000 1.5h2.625v2.625a.75.75 0 001.5 0v-2.625h2.625a.75.75 0 000-1.5h-2.625V13.5z"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View File

@ -0,0 +1,49 @@
<template>
<div class="flex items-center relative">
<!-- v-bind:class="{ 'text-white bg-teal-600 border-teal-600': isCurrent, 'border-teal-600': isChecked }" -->
<div
class="text-gray-500 rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2"
:class="[
isCurrent ? 'text-white bg-teal-600 border-teal-600' : 'border-gray-300',
isChecked && 'text-teal-600 border-teal-600',
]"
>
<!-- <svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"
/>
</svg> -->
<slot></slot>
<div class="absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase invisible sm:visible">
{{ label }}
</div>
</div>
</div>
<div v-if="!isLastStep"
class="flex-auto border-t-2 transition duration-500 ease-in-out invisible sm:visible"
:class="[isChecked ? 'border-teal-600' : 'border-gray-300']"
></div>
</template>
<script>
export default {
name: 'Icon_Multistep',
props: {
isCurrent: Boolean,
isChecked: Boolean,
isLastStep: Boolean,
label: String,
},
data() {
return {
mode: 'light',
checkedClass: 'border-teal-600',
uncheckedClass: 'border-gray-300',
};
},
};
</script>

View File

@ -7,8 +7,8 @@ import {ComputedRef} from "vue";
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { containerMaxW } from '@/config.js'; import { containerMaxW } from '@/config.js';
// import { MainService } from '@/Stores/main.js'; // import { MainService } from '@/Stores/main.js';
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
import { LayoutService } from '@/Stores/layout.js'; import { LayoutService } from '@/Stores/layout';
import { import {
mdiForwardburger, mdiForwardburger,
mdiBackburger, mdiBackburger,

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { StyleService } from '@/Stores/style.js' import { StyleService } from '@/Stores/style'
// import { Link } from '@inertiajs/vue3' // import { Link } from '@inertiajs/vue3'
import { Link } from '@inertiajs/vue3' import { Link } from '@inertiajs/vue3'
import { computed } from 'vue' import { computed } from 'vue'

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { StyleService } from '@/Stores/style.js' import { StyleService } from '@/Stores/style'
import { computed, ref, onMounted, onBeforeUnmount } from 'vue' import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import NavBarItem from '@/Components/NavBarItem.vue' import NavBarItem from '@/Components/NavBarItem.vue'

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
defineProps({ defineProps({
zIndex: { zIndex: {

View File

@ -0,0 +1,281 @@
<template>
<!-- <input
v-model="data.search"
@change="onChange"
type="text"
class="text-base font-medium block w-full rounded-md border transition ease-in-out focus:ring-1 border-gray-300 border-solid py-2 px-3 text-gray-700 placeholder-gray-400 focus:border-blue-200 focus:ring-blue-500 focus:outline-none"
v-bind:name="props.name"
/>
<ul v-if="data.isOpen" class="mt-1 border-2 border-slate-50 overflow-auto shadow-lg rounded list-none">
<li
:class="['hover:bg-blue-100 hover:text-blue-800', 'w-full list-none text-left py-2 px-3 cursor-pointer']"
v-for="(result, i) in data.results"
:key="i"
>
{{ result.name }}
</li>
</ul> -->
<!-- <div class="flex-col justify-center relative"> -->
<div class="relative">
<input
v-model="data.search"
type="text"
:class="inputElClass"
:name="props.name"
:placeholder="placeholder"
autocomplete="off"
@keydown.down="onArrowDown"
@keydown.up="onArrowUp"
/>
<svg
class="w-4 h-4 absolute left-2.5 top-3.5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<!-- <ul v-if="data.isOpen" class="bg-white border border-gray-100 w-full mt-2 max-h-28 overflow-y-auto"> -->
<!-- :ref="(el) => { ul[i] = el }" -->
<ul v-if="data.isOpen" class="bg-white dark:bg-slate-800 w-full mt-2 max-h-28 overflow-y-auto scroll-smooth">
<li
class="pl-8 pr-2 py-1 border-b-2 border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
:class="{
'bg-yellow-50 text-gray-900': i == selectedIndex,
}"
v-for="(result, i) in data.results"
:key="i"
:ref="
(el: HTMLLIElement | null) => {
ul[i] = el;
}
"
>
<svg class="absolute w-4 h-4 left-2 top-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
<!-- <b>Gar</b>{{ result.name }} -->
<!-- <span>
{{ makeBold(result.name) }}
</span> -->
<span
v-for="(item, index) in makeBold(result.name)"
:key="index"
:class="{
'font-bold': data.search.toLowerCase().includes(item.toLowerCase()),
'is-active': index == selectedIndex,
}"
>
{{ item }}
</span>
</li>
</ul>
<!-- </div> -->
</template>
<script setup lang="ts">
import { reactive, ref, computed, Ref, watch } from 'vue';
import axios from 'axios';
let props = defineProps({
name: {
type: String,
required: false,
default: 'autocomplete',
},
source: {
type: [String, Array, Function],
required: true,
default: '',
},
label: {
type: String,
required: false,
default: 'name',
},
responseProperty: {
type: String,
required: false,
default: 'name',
},
placeholder: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
required: Boolean,
borderless: Boolean,
transparent: Boolean,
ctrlKFocus: Boolean,
});
const inputElClass = computed(() => {
const base = [
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
'dark:placeholder-gray-400',
'h-12',
props.borderless ? 'border-0' : 'border',
props.transparent ? 'bg-transparent' : 'bg-white dark:bg-slate-800',
// props.isReadOnly ? 'bg-gray-50 dark:bg-slate-600' : 'bg-white dark:bg-slate-800',
];
// if (props.icon) {
base.push('pl-10');
// }
return base;
});
let search = ref('');
let data = reactive({
search: search,
isOpen: false,
results: [],
});
let error = ref('');
let selectedIndex: Ref<number> = ref(0);
// const listItem = ref(null);
const ul: Ref<Array<HTMLLIElement | null>> = ref([]);
watch(selectedIndex, (selectedIndex) => {
if (selectedIndex != null && ul.value != null) {
const currentElement: HTMLLIElement | null = ul.value[selectedIndex];
currentElement &&
currentElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start',
});
}
});
watch(search, async () => {
await onChange();
});
// function clear() {
// data.search = "";
// data.isOpen = false;
// data.results = [];
// error.value = "";
// // this.$emit("clear");
// }
// function onChange() {
// if (!props.source || !data.search) {
// return false;
// }
// data.isOpen = true;
// arrayLikeSearch(props.source);
// }
async function onChange() {
if (!props.source || !data.search) return false;
selectedIndex.value = 0;
if (data.search.length >= 2) {
data.isOpen = true;
switch (true) {
case typeof props.source === 'string':
return await request(props.source, data.search);
// case typeof props.source === 'function':
// return props.source(data.search).then((response) => {
// data.results = getResults(response);
// });
case Array.isArray(props.source):
return arrayLikeSearch(props.source);
default:
throw new Error('typeof source is ' + typeof props.source);
}
} else {
data.results = [];
data.isOpen = false;
}
}
function getResults(response) {
// if (props.responseProperty) {
// let foundObj;
// JSON.stringify(response, (_, nestedValue) => {
// if (nestedValue && nestedValue[props.responseProperty]) foundObj = nestedValue[props.responseProperty];
// return nestedValue;
// });
// return foundObj;
// }
if (Array.isArray(response)) {
return response;
}
return [];
}
// function setResult(result) {
// data.search = result[props.label];
// data.isOpen = false;
// }
// function request(url) {
// return axios.get(url).then((response) => {
// data.results = getResults(response);
// });
// }
async function request(url, param) {
try {
let response = await searchTerm(url, param);
error.value = '';
data.results = getResults(response);
// this.results = res.data;
// this.loading = false;
} catch (error) {
error.value = error.message;
// this.loading = false;
}
}
async function searchTerm(term: string, param): Promise<any> {
let res = await axios.get(term, { params: { filter: param } });
// console.log(res.data);
return res.data; //.response;//.docs;
}
// async function request(term: string): Promise<any> {
// let res = await axios.get('/api/persons', { params: { filter: term } });
// return res.data; //.response;//.docs;
// }
function arrayLikeSearch(items) {
data.results = items.filter((item) => {
return item.toLowerCase().indexOf(data.search.toLowerCase()) > -1;
});
}
function makeBold(suggestion) {
const query = data.search.valueOf();
const regex = new RegExp(query.split('').join('-?'), 'i');
const test = suggestion.replace(regex, (match) => '<split>' + match + '<split>');
// return suggestion.match(regex);
// const splitWord = suggestion.match(regex);
return test.split('<split>');
}
function onArrowDown() {
if (data.results.length > 0) {
selectedIndex.value = selectedIndex.value === data.results.length - 1 ? 0 : selectedIndex.value + 1;
// const currentElement: HTMLLIElement = ul.value[selectedIndex.value];
}
}
function onArrowUp() {
if (data.results.length > 0) {
selectedIndex.value = selectedIndex.value == 0 || selectedIndex.value == -1 ? data.results.length - 1 : selectedIndex.value - 1;
}
}
</script>

View File

@ -1,9 +1,9 @@
<script setup> <script setup>
import { containerMaxW } from '@/config.js' import { containerMaxW } from '@/config.js';
</script> </script>
<template> <template>
<section class="p-6" v-bind:class="containerMaxW"> <section class="p-6" v-bind:class="containerMaxW">
<slot /> <slot />
</section> </section>
</template> </template>

30
resources/js/Dataset.ts Normal file
View File

@ -0,0 +1,30 @@
import { Ref } from 'vue';
export interface Dataset {
[key: string]: string | Ref<string>| boolean | Array<Title> | (IErrorMessage | undefined);
language: Ref<string>;
// licenses: Array<number>;
rights: boolean;
type: string;
creating_corporation: string;
titles: Array<Title>;
descriptions: Array<Description>;
errors?: IErrorMessage;
// async (user): Promise<void>;
}
export interface Title {
value: string;
type: string;
language: string | Ref<string>;
}
export interface Description {
value: string;
type: string;
language: string | Ref<string>;
}
interface IErrorMessage {
[key: string]: Array<string>;
}

View File

@ -1,33 +1,37 @@
<script lang="ts" setup> <script lang="ts" setup>
import { LayoutService } from '@/Stores/layout.js' import { LayoutService } from '@/Stores/layout';
import { StyleService } from '@/Stores/style' import { StyleService } from '@/Stores/style';
import NavBar from '@/Components/NavBar.vue' import NavBar from '@/Components/NavBar.vue';
import AsideMenu from '@/Components/AsideMenu.vue' import AsideMenu from '@/Components/AsideMenu.vue';
import FooterBar from '@/Components/FooterBar.vue' import FooterBar from '@/Components/FooterBar.vue';
const styleService = StyleService() const styleService = StyleService();
const layoutService = LayoutService() const layoutService = LayoutService();
// defineProps({ // defineProps({
// user: { // user: {
// type: Object, // type: Object,
// default: () => ({}), // default: () => ({}),
// } // }
// }); // });
</script> </script>
<template> <template>
<div :class="{ 'dark': styleService.darkMode, 'overflow-hidden lg:overflow-visible': layoutService.isAsideMobileExpanded }">
<div <div
:class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }" :class="{
class="pt-14 xl:pl-60 min-h-screen w-screen transition-position lg:w-auto bg-gray-50 dark:bg-slate-800 dark:text-slate-100" 'dark': styleService.darkMode,
'overflow-hidden lg:overflow-visible': layoutService.isAsideMobileExpanded,
}"
> >
<NavBar :class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }" /> <div
<AsideMenu /> :class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }"
<slot></slot> class="pt-14 xl:pl-60 min-h-screen w-screen transition-position lg:w-auto bg-gray-50 dark:bg-slate-800 dark:text-slate-100"
<FooterBar /> >
<NavBar :class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }" />
<AsideMenu />
<slot></slot>
<FooterBar />
</div>
</div> </div>
</div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
const styleService = StyleService(); const styleService = StyleService();
</script> </script>

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
import { import {
mdiContrastCircle, mdiContrastCircle,

View File

@ -1,6 +1,7 @@
<script setup> <script setup lang="ts">
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3'; // import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
import { Head, Link, useForm, router } from '@inertiajs/vue3'; import { useForm } from '@inertiajs/vue3';
// import { reactive } from 'vue';
import { import {
mdiAccount, mdiAccount,
mdiAccountCircle, mdiAccountCircle,
@ -9,7 +10,7 @@ import {
mdiAsterisk, mdiAsterisk,
mdiFormTextboxPassword, mdiFormTextboxPassword,
mdiArrowLeftBoldOutline, mdiArrowLeftBoldOutline,
mdiAlertBoxOutline, // mdiAlertBoxOutline,
} from '@mdi/js'; } from '@mdi/js';
import SectionMain from '@/Components/SectionMain.vue'; import SectionMain from '@/Components/SectionMain.vue';
import CardBox from '@/Components/CardBox.vue'; import CardBox from '@/Components/CardBox.vue';
@ -18,7 +19,7 @@ import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue'; import FormControl from '@/Components/FormControl.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 NotificationBar from '@/Components/NotificationBar.vue'; // import NotificationBar from '@/Components/NotificationBar.vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue'; import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue'; import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import { stardust } from '@eidellev/adonis-stardust/client'; import { stardust } from '@eidellev/adonis-stardust/client';
@ -41,16 +42,16 @@ const profileForm = useForm({
email: props.user.email, email: props.user.email,
}); });
const profileSubmit = async () => { const profileSubmit = async () => {
await router.post(stardust.route('admin.account.info.store', [props.user.id]), profileForm); await profileForm.post(stardust.route('admin.account.info.store', [props.user.id]));
}; };
const passwordForm = useForm({ const passwordForm = useForm({
old_password: null, old_password: "",
new_password: null, new_password: "",
confirm_password: null, confirm_password: "",
}); });
const passwordSubmit = async () => { const passwordSubmit = async () => {
await router.post(stardust.route('admin.account.info.store'), passwordForm, { await passwordForm.post(stardust.route('admin.account.info.store'), {
preserveScroll: true, preserveScroll: true,
onSuccess: (resp) => { onSuccess: (resp) => {
console.log(resp); console.log(resp);
@ -68,9 +69,9 @@ const passwordSubmit = async () => {
rounded-full small /> rounded-full small />
</SectionTitleLineWithButton> </SectionTitleLineWithButton>
<NotificationBar v-if="$page.props.flash.message" color="success" :icon="mdiAlertBoxOutline"> <!-- <NotificationBar v-if="$page.props.flash.message" color="success" :icon="mdiAlertBoxOutline">
{{ $page.props.flash.message }} {{ $page.props.flash.message }}
</NotificationBar> </NotificationBar> -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">

View File

@ -1,5 +1,5 @@
<script setup> <script setup lang="ts">
import { Head, Link, useForm, router } from '@inertiajs/vue3'; import { Head, useForm, router } from '@inertiajs/vue3';
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js'; import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue'; import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import SectionMain from '@/Components/SectionMain.vue'; import SectionMain from '@/Components/SectionMain.vue';
@ -45,11 +45,11 @@ const submit = async () => {
<SectionMain> <SectionMain>
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Add user" main> <SectionTitleLineWithButton :icon="mdiAccountKey" title="Add user" main>
<BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back" <BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back"
color="white" rounded-full small /> color="modern" rounded-full small />
</SectionTitleLineWithButton> </SectionTitleLineWithButton>
<!-- @submit.prevent="form.post(stardust.route('user.store'))" --> <!-- @submit.prevent="form.post(stardust.route('user.store'))" -->
<CardBox form @submit.prevent="submit()"> <CardBox form @submit.prevent="submit()">
<FormField label="Login" :class="{ 'text-red-400': errors.name }"> <FormField label="Login" :class="{ 'text-red-400': errors.login }">
<FormControl v-model="form.login" type="text" placeholder="Enter Login" :errors="errors.login"> <FormControl v-model="form.login" type="text" placeholder="Enter Login" :errors="errors.login">
<div class="text-red-400 text-sm" v-if="errors.login && Array.isArray(errors.login)"> <div class="text-red-400 text-sm" v-if="errors.login && Array.isArray(errors.login)">
<!-- {{ errors.login }} --> <!-- {{ errors.login }} -->

View File

@ -1,5 +1,5 @@
<script setup> <script setup lang="ts">
import { Head, Link, useForm, router } from "@inertiajs/vue3" import { Head, useForm, router } from "@inertiajs/vue3"
import { import {
mdiAccountKey, mdiAccountKey,
mdiArrowLeftBoldOutline mdiArrowLeftBoldOutline

View File

@ -1,6 +1,7 @@
<script setup> <script setup lang="ts">
// import { Head, Link, useForm, usePage } from '@inertiajs/inertia-vue3'; // import { Head, Link, useForm, usePage } from '@inertiajs/inertia-vue3';
import { Head, Link, useForm, usePage } from '@inertiajs/vue3'; import { Head, Link, useForm, usePage } from '@inertiajs/vue3';
import { ComputedRef } from 'vue';
import { import {
mdiAccountKey, mdiAccountKey,
mdiPlus, mdiPlus,
@ -8,7 +9,7 @@ import {
mdiTrashCan, mdiTrashCan,
mdiAlertBoxOutline, mdiAlertBoxOutline,
} from '@mdi/js'; } from '@mdi/js';
import { watch, computed } from 'vue'; import { computed } from 'vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue'; import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.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';
@ -44,7 +45,7 @@ const props = defineProps({
// return props.filters.search; // return props.filters.search;
// }); // });
const flash = computed(() => { const flash: ComputedRef<any> = computed(() => {
// let test = usePage(); // let test = usePage();
// console.log(test); // console.log(test);
return usePage().props.flash; return usePage().props.flash;
@ -73,7 +74,7 @@ const destroy = async (id) => {
<SectionMain> <SectionMain>
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Tethys Users" main> <SectionTitleLineWithButton :icon="mdiAccountKey" title="Tethys Users" main>
<BaseButton v-if="can.create" :route-name="stardust.route('user.create')" :icon="mdiPlus" label="Add" <BaseButton v-if="can.create" :route-name="stardust.route('user.create')" :icon="mdiPlus" label="Add"
color="info" rounded-full small /> color="modern" rounded-full small />
</SectionTitleLineWithButton> </SectionTitleLineWithButton>
<!-- <label>{{ form.search }}</label> --> <!-- <label>{{ form.search }}</label> -->
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline"> <NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
@ -87,7 +88,7 @@ const destroy = async (id) => {
<input type="search" v-model="form.search" <input type="search" v-model="form.search"
class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
placeholder="Search" /> placeholder="Search" />
<BaseButton label="Search" type="submit" color="info" <BaseButton label="Search" type="submit" color="modern"
class="ml-4 inline-flex items-center px-4 py-2" /> class="ml-4 inline-flex items-center px-4 py-2" />
</div> </div>
</div> </div>

View File

@ -1,92 +1,67 @@
<script setup> <script setup lang="ts">
import { Head, Link, useForm } from "@inertiajs/vue3" import { Head } from '@inertiajs/vue3';
import { import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
mdiAccountKey, import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
mdiArrowLeftBoldOutline, import SectionMain from '@/Components/SectionMain.vue';
} from "@mdi/js" import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import LayoutAuthenticated from "@/Layouts/LayoutAuthenticated.vue" import CardBox from '@/Components/CardBox.vue';
import SectionMain from "@/Components/SectionMain.vue" import BaseButton from '@/Components/BaseButton.vue';
import SectionTitleLineWithButton from "@/Components/SectionTitleLineWithButton.vue"
import CardBox from "@/Components/CardBox.vue"
import BaseButton from "@/Components/BaseButton.vue"
import { stardust } from '@eidellev/adonis-stardust/client'; import { stardust } from '@eidellev/adonis-stardust/client';
const props = defineProps({ defineProps({
user: { user: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
roles: { roles: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
userHasRoles: { userHasRoles: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
} },
}) });
</script> </script>
<template> <template>
<LayoutAuthenticated :user="user"> <LayoutAuthenticated :user="user">
<Head title="View user" />
<Head title="View user" /> <SectionMain>
<SectionMain> <SectionTitleLineWithButton :icon="mdiAccountKey" title="View user" main>
<SectionTitleLineWithButton :icon="mdiAccountKey" title="View user" main> <BaseButton
<BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back" color="white" :route-name="stardust.route('user.index')"
rounded-full small /> :icon="mdiArrowLeftBoldOutline"
</SectionTitleLineWithButton> label="Back"
<CardBox class="mb-6"> color="white"
<table> rounded-full
<tbody> small
<tr> />
<td class=" </SectionTitleLineWithButton>
p-4 <CardBox class="mb-6">
pl-8 <table>
text-slate-500 <tbody>
dark:text-slate-400 <tr>
hidden <td class="p-4 pl-8 text-slate-500 dark:text-slate-400 hidden lg:block">Login</td>
lg:block <td data-label="Login">
"> {{ user.login }}
Login </td>
</td> </tr>
<td data-label="Login"> <tr>
{{ user.login }} <td class="p-4 pl-8 text-slate-500 dark:text-slate-400 hidden lg:block">Email</td>
</td> <td data-label="Email">
</tr> {{ user.email }}
<tr> </td>
<td class=" </tr>
p-4 <tr>
pl-8 <td class="p-4 pl-8 text-slate-500 dark:text-slate-400 hidden lg:block">Created</td>
text-slate-500 <td data-label="Created">
dark:text-slate-400 {{ new Date(user.created_at).toLocaleString() }}
hidden </td>
lg:block </tr>
"> </tbody>
Email </table>
</td> </CardBox>
<td data-label="Email"> </SectionMain>
{{ user.email }} </LayoutAuthenticated>
</td>
</tr>
<tr>
<td class="
p-4
pl-8
text-slate-500
dark:text-slate-400
hidden
lg:block
">
Created
</td>
<td data-label="Created">
{{ new Date(user.created_at).toLocaleString() }}
</td>
</tr>
</tbody>
</table>
</CardBox>
</SectionMain>
</LayoutAuthenticated>
</template> </template>

View File

@ -31,60 +31,70 @@ import FormControl from '@/Components/FormControl.vue';
</script> --> </script> -->
<template> <template>
<LayoutGuest> <LayoutGuest>
<Head title="Login" />
<Head title="Login" /> <SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'">
<CardBox :class="cardClass" form @submit.prevent="submit">
<FormValidationErrors v-bind:errors="errors" />
<SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'"> <NotificationBarInCard v-if="status" color="info">
<CardBox :class="cardClass" form @submit.prevent="submit"> {{ status }}
<FormValidationErrors v-bind:errors="errors" /> </NotificationBarInCard>
<NotificationBarInCard v-if="status" color="info"> <FormField label="Email" label-for="email" help="Please enter your email">
{{ status }} <FormControl v-model="form.email" :icon="mdiAccount" id="email" autocomplete="email" type="email" required />
</NotificationBarInCard> </FormField>
<FormField label="Email" label-for="email" help="Please enter your email"> <FormField label="Password" label-for="password" help="Please enter your password">
<FormControl v-model="form.email" :icon="mdiAccount" id="email" autocomplete="email" type="email" <FormControl
required /> v-model="form.password"
</FormField> :icon="mdiAsterisk"
type="password"
id="password"
autocomplete="current-password"
required
/>
</FormField>
<FormField label="Password" label-for="password" help="Please enter your password"> <FormCheckRadioGroup v-model="form.remember" name="remember" :options="{ remember: 'Remember' }" />
<FormControl v-model="form.password" :icon="mdiAsterisk" type="password" id="password"
autocomplete="current-password" required />
</FormField>
<FormCheckRadioGroup v-model="form.remember" name="remember" :options="{ remember: 'Remember' }" /> <!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
<!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
{{ flash.message }} {{ flash.message }}
class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4"
</NotificationBar> --> </NotificationBar> -->
<div v-if="flash && flash.message" class="flex flex-col mt-6 animate-fade-in"> <div v-if="flash && flash.message" class="flex flex-col mt-6 animate-fade-in">
<div class="bg-yellow-500 border-l-4 border-orange-400 text-white p-4" role="alert"> <div class="bg-yellow-500 border-l-4 border-orange-400 text-white p-4" role="alert">
<p class="font-bold">Be Warned</p> <p class="font-bold">Be Warned</p>
<p>{{ flash.message }}</p> <p>{{ flash.message }}</p>
</div> </div>
</div> </div>
<BaseDivider /> <BaseDivider />
<!-- buttons --> <!-- buttons -->
<BaseLevel> <BaseLevel>
<BaseButtons> <BaseButtons>
<BaseButton type="submit" color="info" label="Login" :class="{ 'opacity-25': form.processing }" <BaseButton
v-bind:disabled="form.processing" /> type="submit"
<!-- <BaseButton v-if="canResetPassword" :route-name="route('password.request')" color="info" outline color="info"
label="Login"
:class="{ 'opacity-25': form.processing }"
v-bind:disabled="form.processing"
/>
<!-- <BaseButton v-if="canResetPassword" :route-name="route('password.request')" color="info" outline
label="Remind" /> --> label="Remind" /> -->
</BaseButtons> </BaseButtons>
<Link :href="stardust.route('app.register.show')"> Register </Link> <!-- <Link :href="stardust.route('app.register.show')"> Register </Link> -->
</BaseLevel> </BaseLevel>
</CardBox> </CardBox>
</SectionFullScreen> </SectionFullScreen>
</LayoutGuest> </LayoutGuest>
</template> </template>
<script setup> <script setup lang="ts">
import { useForm, Head, Link } from '@inertiajs/vue3'; import { useForm, Head } from '@inertiajs/vue3';
import { Ref } from 'vue';
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3'; // import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
import { mdiAccount, mdiAsterisk } from '@mdi/js'; import { mdiAccount, mdiAsterisk } from '@mdi/js';
import LayoutGuest from '@/Layouts/LayoutGuest.vue'; import LayoutGuest from '@/Layouts/LayoutGuest.vue';
@ -101,45 +111,45 @@ import NotificationBarInCard from '@/Components/NotificationBarInCard.vue';
import BaseLevel from '@/Components/BaseLevel.vue'; import BaseLevel from '@/Components/BaseLevel.vue';
import { stardust } from '@eidellev/adonis-stardust/client'; import { stardust } from '@eidellev/adonis-stardust/client';
import NotificationBar from '@/Components/NotificationBar.vue'; // import NotificationBar from '@/Components/NotificationBar.vue';
import { computed } from 'vue'; import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3'; import { usePage } from '@inertiajs/vue3';
// interface IErrorMessage { // interface IErrorMessage {
// [key: string]: Array<string>; // [key: string]: Array<string>;
// } // }
const flash = computed(() => { const flash: Ref<any> = computed(() => {
return usePage().props.flash; return usePage().props.flash;
}); });
const props = defineProps({ defineProps({
canResetPassword: Boolean, canResetPassword: Boolean,
status: { status: {
type: String, type: String,
default: null, default: null,
}, },
errors: { errors: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}); });
const form = useForm({ const form = useForm({
email: '', email: '',
password: '', password: '',
remember: [], remember: [],
}); });
const submit = async() => { const submit = async () => {
await form await form
.transform((data) => ({ .transform((data) => ({
...data, ...data,
remember: form.remember && form.remember.length ? 'on' : '', remember: form.remember && form.remember.length ? 'on' : '',
})) }))
.post(stardust.route('login.store'), { .post(stardust.route('login.store'), {
// onFinish: () => { onFinish: () => {
// form.reset('password'); form.reset('password');
// } },
}); });
}; };
</script> </script>

View File

@ -1,10 +1,10 @@
<script setup> <script setup lang="ts">
import { Head } from '@inertiajs/vue3'; import { Head } from '@inertiajs/vue3';
import { computed, ref, onMounted } from 'vue'; import { computed, ref, onMounted } from 'vue';
import { MainService } from '@/Stores/main'; import { MainService } from '@/Stores/main';
// import { Inertia } from '@inertiajs/inertia';
import { import {
mdiAccountMultiple, mdiAccountMultiple,
mdiCartOutline,
mdiDatabaseOutline, mdiDatabaseOutline,
mdiChartTimelineVariant, mdiChartTimelineVariant,
mdiFinance, mdiFinance,
@ -13,7 +13,7 @@ import {
mdiGithub, mdiGithub,
mdiChartPie, mdiChartPie,
} from '@mdi/js'; } from '@mdi/js';
import { containerMaxW } from '@/config.js'; // "xl:max-w-6xl xl:mx-auto" // import { containerMaxW } from '@/config.js'; // "xl:max-w-6xl xl:mx-auto"
import * as chartConfig from '@/Components/Charts/chart.config.js'; import * as chartConfig from '@/Components/Charts/chart.config.js';
import LineChart from '@/Components/Charts/LineChart.vue'; import LineChart from '@/Components/Charts/LineChart.vue';
import SectionMain from '@/Components/SectionMain.vue'; import SectionMain from '@/Components/SectionMain.vue';
@ -27,7 +27,7 @@ import CardBoxClient from '@/Components/CardBoxClient.vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue'; import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue'; import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import SectionBannerStarOnGitHub from '@/Components/SectionBannerStarOnGitea.vue'; import SectionBannerStarOnGitHub from '@/Components/SectionBannerStarOnGitea.vue';
const chartData = ref(null); const chartData = ref();
const fillChartData = () => { const fillChartData = () => {
chartData.value = chartConfig.sampleChartData(); chartData.value = chartConfig.sampleChartData();
}; };
@ -42,7 +42,7 @@ mainService.fetch('history');
mainService.fetchApi('authors'); mainService.fetchApi('authors');
mainService.fetchApi('datasets'); mainService.fetchApi('datasets');
const clientBarItems = computed(() => mainService.clients.slice(0, 4)); // const clientBarItems = computed(() => mainService.clients.slice(0, 4));
const transactionBarItems = computed(() => mainService.history); const transactionBarItems = computed(() => mainService.history);
const authorBarItems = computed(() => mainService.authors.slice(0, 4)); const authorBarItems = computed(() => mainService.authors.slice(0, 4));
@ -109,9 +109,9 @@ const datasets = computed(() => mainService.datasets);
<SectionTitleLineWithButton :icon="mdiAccountMultiple" title="Submitters (to do)" /> <SectionTitleLineWithButton :icon="mdiAccountMultiple" title="Submitters (to do)" />
<!-- <NotificationBar color="info" :icon="mdiMonitorCellphone"> <NotificationBar color="info" :icon="mdiMonitorCellphone">
<b>Responsive table.</b> Collapses on mobile <b>Responsive table.</b> Collapses on mobile
</NotificationBar> --> </NotificationBar>
<CardBox :icon="mdiMonitorCellphone" title="Responsive table" has-table> <CardBox :icon="mdiMonitorCellphone" title="Responsive table" has-table>
<TableSampleClients /> <TableSampleClients />

View File

@ -0,0 +1,604 @@
<script setup lang="ts">
import { Head, useForm } from '@inertiajs/vue3';
import { ref } from 'vue';
import { Dataset, Title } from '@/Dataset';
import { mdiDatabasePlus, mdiMinusCircle, mdiPlusCircle, mdiFinance, mdiInformationOutline, mdiBookOpenPageVariant } from '@mdi/js';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import SectionMain from '@/Components/SectionMain.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import CardBox from '@/Components/CardBox.vue';
import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
// import BaseDivider from '@/Components/BaseDivider.vue';
import BaseButton from '@/Components/BaseButton.vue';
// import BaseButtons from '@/Components/BaseButtons.vue';
import { stardust } from '@eidellev/adonis-stardust/client';
// import { Inertia } from '@inertiajs/inertia';
import CardBoxModal from '@/Components/CardBoxModal.vue';
import BaseIcon from '@/Components/BaseIcon.vue';
import IconWizard from '@/Components/Icons/Wizard.vue';
import IconMandatory from '@/Components/Icons/Mandatory.vue';
import IconLanguage from '@/Components/Icons/Language.vue';
import IconRecommendet from '@/Components/Icons/Recommendet.vue';
import IconConfirm from '@/Components/Icons/Confirm.vue';
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
const props = defineProps({
licenses: {
type: Object,
default: () => ({}),
},
doctypes: {
type: Object,
default: () => ({}),
},
titletypes: {
type: Object,
default: () => ({}),
},
descriptiontypes: {
type: Object,
default: () => ({}),
},
errors: {
type: Object,
default: () => ({}),
},
});
// const form = useForm({
// language: '',
// licenses: [],
// type: '',
// titles: [{ value: '', type: 'Main', language: Dataset.language }],
// });
// let language: (string | Ref<string>) = ref('');
let language = ref('');
let dataset = {
language: language,
licenses: [],
rights: false,
type: '',
creating_corporation: 'Tethys RDR',
titles: [{ value: '', type: 'Main', language: language }],
descriptions: [{ value: '', type: 'Abstract', language: language }],
// errors: undefined,
};
// const form = useForm({
// language: language,
// licenses: [],
// rights: false,
// type: '',
// creating_corporation: 'Tethys RDR',
// titles: [{ value: '', type: 'Main', language: language }],
// descriptions: [{ value: '', type: 'Abstract', language: language }],
// });
const form = useForm<Dataset>(dataset);
// dataset.language.value = 'de';
// const emit = defineEmits(['update:modelValue', 'setRef']);
// computed({
// get: () => form.rights,
// set: (value) => {
// emit('update:modelValue', value);
// },
// });
const isModalActive = ref(false);
const formStep = ref(1);
// const submit = async () => {
// await router.post(stardust.route('user.store'), form, {
// onSuccess: () => {
// form.reset(), (formStep.value = 1);
// },
// });
// };
const nextStep = async () => {
let route = stardust.route('dataset.first.step');
if (formStep.value == 2) {
route = stardust.route('dataset.second.step');
}
// formStep.value++;
await form
.transform((data) => ({
...data,
rights: form.rights && form.rights == true ? 'true' : 'false',
}))
.post(route, {
form,
onSuccess: () => {
formStep.value++;
},
});
};
const prevStep = () => {
formStep.value--;
};
const addTitle = () => {
let newTitle: Title = { value: '', language: '', type: '' };
//this.dataset.files.push(uploadedFiles[i]);
form.titles.push(newTitle);
};
const removeTitle = (key) => {
form.titles.splice(key, 1);
};
const addDescription = () => {
let newDescription = { value: '', language: '', type: '' };
//this.dataset.files.push(uploadedFiles[i]);
form.descriptions.push(newDescription);
};
const removeDescription = (key) => {
form.descriptions.splice(key, 1);
};
</script>
<template>
<CardBoxModal v-model="isModalActive" title="Einverständniserklärung *">
Mit dem Setzen des Hakens bestätige ich hiermit
<ul class="list-decimal">
<li>
die Data Policy von Tethys RDR sowie die Terms & Conditions von Tethys gelesen und verstanden zu haben (<a
href="/docs/HandbuchTethys.pdf"
target="_blank"
>siehe hier</a
>)
</li>
<li>das Einverständnis aller Co-Autoren über die bevorstehende Datenpublikation schriftlich eingeholt zu haben</li>
<li>sowohl mit der Data Policy als auch mit den Terms & Conditions einverstanden zu sein</li>
</ul>
</CardBoxModal>
<LayoutAuthenticated>
<Head title="Submit Dataset" />
<SectionMain>
<SectionTitleLineWithButton :icon="mdiDatabasePlus" title="Submit dataset" main>
<!-- <BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back"
color="white" rounded-full small /> -->
</SectionTitleLineWithButton>
<CardBox>
<div class="mx-4 p-4">
<div class="flex items-center">
<!-- <label>{{ form.titles[0].language }}</label>
<label>{{ form.language }}</label> -->
<icon-wizard :is-current="formStep == 1" :is-checked="formStep > 1" :label="'Language'">
<icon-language></icon-language>
</icon-wizard>
<icon-wizard :is-current="formStep == 2" :is-checked="formStep > 2" :label="'Mandatory'">
<icon-mandatory></icon-mandatory>
</icon-wizard>
<icon-wizard :is-current="formStep == 3" :is-checked="formStep > 3" :label="'Recommendet'">
<icon-recommendet></icon-recommendet>
</icon-wizard>
<icon-wizard :is-current="formStep == 4" :is-checked="false" :label="'Confirm'" :is-last-step="true">
<icon-confirm></icon-confirm>
</icon-wizard>
</div>
</div>
<!-- mt-8: margin-top: 2rem; /* 32px */ 4 p-4: spacing 1rem 16px-->
<div class="mt-8 p-4">
<div v-if="formStep == 1">
<div class="flex flex-col md:flex-row">
<FormField
label="Language *"
help="required: select dataset main language"
:class="{ 'text-red-400': errors.language }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.language"
:type="'select'"
placeholder="[Enter Language]"
:errors="form.errors.language"
:options="['de', 'en']"
>
<div class="text-red-400 text-sm" v-if="form.errors.language">
{{ form.errors.language.join(', ') }}
</div>
</FormControl>
</FormField>
</div>
<FormField
label="Licenses"
wrap-body
:class="{ 'text-red-400': form.errors.licenses }"
class="mt-8 w-full mx-2 flex-1"
>
<FormCheckRadioGroup v-model="form.licenses" name="roles" is-column :options="props.licenses" />
</FormField>
<!-- <label for="rights">
<input class="form-checkbox" name="rights" id="rights" type="checkbox" v-model="dataset.rights" />
terms and conditions
</label> -->
<FormField
label="Rights"
help="You must agree to continue"
wrap-body
:class="{ 'text-red-400': form.errors.rights }"
class="mt-8 w-full mx-2 flex-1 flex-col"
>
<label for="rights" class="checkbox mr-6 mb-3 last:mr-0">
<input type="checkbox" id="rights" required v-model="form.rights" />
<span class="check" />
<a class="pl-2" target="_blank">terms and conditions </a>
<!-- <BaseButton color="modern" :icon="mdiInformationOutline" small @click="isModalActive = true" /> -->
<BaseIcon
v-if="mdiInformationOutline"
:path="mdiInformationOutline"
@click.prevent="isModalActive = true"
/>
</label>
</FormField>
<div class="text-red-400 text-sm" v-if="errors.rights && Array.isArray(errors.rights)">
<!-- {{ errors.password_confirmation }} -->
{{ errors.rights.join(', ') }}
</div>
</div>
<div v-if="formStep == 2">
<!-- <CardBox title="Performance" :icon="mdiFinance" :header-icon="mdiReload" class="mb-6"> -->
<div class="flex flex-col md:flex-row">
<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>
<!-- <div class="w-full mx-2 flex-1 svelte-1l8159u"></div> -->
<!-- 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 /> -->
<!-- titles -->
<CardBox
class="mb-6"
:has-form-data="true"
title="Titles"
:icon="mdiFinance"
:header-icon="mdiPlusCircle"
v-on:header-icon-click="addTitle()"
>
<!-- <div class="py-6 border-t border-gray-100 dark:border-slate-800"> -->
<div class="flex flex-col md:flex-row">
<FormField
label="Main Title *"
help="required: main title"
:class="{ 'text-red-400': form.errors['titles.0.value'] }"
class="w-full mx-2 flex-1"
>
<FormControl required v-model="form.titles[0].value" type="text" placeholder="[enter main title]">
<div
class="text-red-400 text-sm"
v-if="form.errors['titles.0.value'] && Array.isArray(form.errors['titles.0.value'])"
>
{{ form.errors['titles.0.value'].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Main Title Language*"
help="required: main title language"
:class="{ 'text-red-400': form.errors['titles.0.language'] }"
class="w-full mx-2 flex-1"
>
<FormControl required v-model="form.titles[0].language" type="text" :is-read-only="true">
<div
class="text-red-400 text-sm"
v-if="form.errors['titles.0.language'] && Array.isArray(form.errors['titles.0.language'])"
>
{{ form.errors['titles.0.language'].join(', ') }}
</div>
</FormControl>
</FormField>
</div>
<label v-if="form.titles.length > 1">additional titles </label>
<!-- <BaseButton :icon="mdiPlusCircle" @click.prevent="addTitle()" color="modern" rounded-full small /> -->
<div v-if="form.titles.length > 1">
<div v-for="(item, index) in form.titles">
<div class="flex flex-col md:flex-row" v-if="item.type != 'Main'">
<FormField v-if="item.type != 'Main'" label="Remove">
<BaseButton
:icon="mdiMinusCircle"
class="mt-1"
@click.prevent="removeTitle(index)"
color="modern"
small
/>
</FormField>
<FormField
label="Title Value *"
:class="{ 'text-red-400': form.errors[`titles.${index}.value`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.titles[index].value"
type="text"
placeholder="[enter main title]"
>
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.value`]">
{{ form.errors[`titles.${index}.value`].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Title Type*"
:class="{ 'text-red-400': form.errors[`titles.${index}.type`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.titles[index].type"
type="select"
:options="titletypes"
placeholder="[select title type]"
>
<div class="text-red-400 text-sm" v-if="Array.isArray(form.errors[`titles.${index}.type`])">
{{ form.errors[`titles.${index}.type`].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Title Language*"
:class="{ 'text-red-400': form.errors[`titles.${index}.language`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.titles[index].language"
type="select"
:options="['de', 'en']"
placeholder="[select title language]"
>
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.language`]">
{{ form.errors[`titles.${index}.language`].join(', ') }}
</div>
</FormControl>
</FormField>
</div>
</div>
</div>
<!-- </div> -->
</CardBox>
<!-- Descriptions -->
<CardBox
class="mb-6"
:has-form-data="true"
title="Descriptions"
:header-icon="mdiPlusCircle"
v-on:header-icon-click="addDescription()"
>
<div class="flex flex-col md:flex-row">
<FormField
label="Main Abstract *"
help="required: main abstract"
:class="{ 'text-red-400': form.errors['descriptions.0.value'] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.descriptions[0].value"
type="textarea"
placeholder="[enter main abstract]"
>
<div
class="text-red-400 text-sm"
v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.value'])"
>
{{ form.errors['descriptions.0.value'].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Main Title Language*"
help="required: main abstract language"
:class="{ 'text-red-400': form.errors['descriptions.0.language'] }"
class="w-full mx-2 flex-1"
>
<FormControl required v-model="form.descriptions[0].language" type="text" :is-read-only="true">
<div
class="text-red-400 text-sm"
v-if="
form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.language'])
"
>
{{ form.errors['descriptions.0.language'].join(', ') }}
</div>
</FormControl>
</FormField>
</div>
<label v-if="form.descriptions.length > 1">additional descriptions: </label>
<!-- <BaseButton :icon="mdiPlusCircle" @click.prevent="addTitle()" color="modern" rounded-full small /> -->
<div v-if="form.descriptions.length > 1">
<div v-for="(item, index) in form.descriptions">
<div class="flex flex-col md:flex-row" v-if="item.type != 'Abstract'">
<FormField v-if="item.type != 'Abstract'" label="Remove">
<BaseButton
:icon="mdiMinusCircle"
class="mt-1"
@click.prevent="removeDescription(index)"
color="modern"
small
/>
</FormField>
<FormField
label="Description Value *"
:class="{ 'text-red-400': form.errors[`descriptions.${index}.value`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.descriptions[index].value"
type="text"
placeholder="[enter additional description]"
>
<div
class="text-red-400 text-sm"
v-if="
form.errors[`descriptions.${index}.value`] &&
Array.isArray(form.errors[`descriptions.${index}.value`])
"
>
{{ form.errors[`descriptions.${index}.value`].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Description Type *"
:class="{ 'text-red-400': form.errors[`descriptions.${index}.type`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.descriptions[index].type"
type="select"
:options="descriptiontypes"
placeholder="[select description type]"
>
<div
class="text-red-400 text-sm"
v-if="
form.errors[`descriptions.${index}.type`] &&
Array.isArray(form.errors[`descriptions.${index}.type`])
"
>
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
</div>
</FormControl>
</FormField>
<FormField
label="Description Language*"
:class="{ 'text-red-400': form.errors[`titdescriptionsles.${index}.language`] }"
class="w-full mx-2 flex-1"
>
<FormControl
required
v-model="form.descriptions[index].language"
type="select"
:options="['de', 'en']"
placeholder="[select title language]"
>
<div
class="text-red-400 text-sm"
v-if="form.errors && Array.isArray(form.errors[`descriptions.${index}.language`])"
>
{{ form.errors[`descriptions.${index}.language`].join(', ') }}
</div>
</FormControl>
</FormField>
</div>
</div>
</div>
</CardBox>
<CardBox class="mb-6" :has-form-data="true" title="Authors" :icon="mdiBookOpenPageVariant" >
<SearchAutocomplete
source="/api/persons"
:response-property="'first_name'"
placeholder="search in person table...."
></SearchAutocomplete>
</CardBox>
<!-- <SectionTitleLineWithButton :icon="mdiChartPie" title="Trends overview (to do publications per year)" class="flex flex-col md:flex-row" >
</SectionTitleLineWithButton> -->
</div>
<div v-if="formStep == 3">
<label>To Do: Recommendet</label>
<!-- <div class="w-full mx-2 flex-1 svelte-1l8159u">
<div class="font-bold h-6 mt-3 text-gray-600 text-xs leading-8 uppercase">Username</div>
<div class="bg-white my-2 p-1 flex border border-gray-200 rounded svelte-1l8159u">
<input placeholder="Just a hint.." class="p-1 px-2 appearance-none outline-none w-full text-gray-800" />
</div>
</div>
<div class="w-full mx-2 flex-1 svelte-1l8159u">
<div class="font-bold h-6 mt-3 text-gray-600 text-xs leading-8 uppercase">Your Email</div>
<div class="bg-white my-2 p-1 flex border border-gray-200 rounded svelte-1l8159u">
<input placeholder="jhon@doe.com" class="p-1 px-2 appearance-none outline-none w-full text-gray-800" />
</div>
</div> -->
</div>
<div v-if="formStep == 4">
<label>To Do: File Upload</label>
</div>
</div>
<template #footer>
<div class="flex p-2 mt-4">
<button
v-if="formStep > 1"
@click="prevStep"
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-gray-200 bg-gray-100 text-gray-700 border duration-200 ease-in-out border-gray-600 transition"
>
Previous
</button>
<div class="flex-auto flex flex-row-reverse">
<button
v-if="formStep < 4"
@click="nextStep"
class="text-base ml-2 hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-600 bg-teal-600 text-teal-100 border duration-200 ease-in-out border-teal-600 transition"
>
Next
</button>
<!-- <button
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"
>
Skip
</button> -->
</div>
</div>
</template>
</CardBox>
</SectionMain>
</LayoutAuthenticated>
</template>

View File

@ -1,62 +0,0 @@
import { defineStore } from 'pinia';
import axios from 'axios';
export const MainService = defineStore('main', {
state: () => ({
/* User */
userName: '',
userEmail: null,
userAvatar: null,
/* Field focus with ctrl+k (to register only once) */
isFieldFocusRegistered: false,
/* Sample data for starting dashboard(commonly used) */
clients: [],
history: [],
authors: [],
datasets: []
}),
actions: {
// payload = authenticated user
setUser(payload) {
if (payload.name) {
this.userName = payload.name;
}
if (payload.email) {
this.userEmail = payload.email;
}
if (payload.avatar) {
this.userAvatar = payload.avatar;
}
},
fetch(sampleDataKey) {
// sampleDataKey= clients or history
axios
.get(`data-sources/${sampleDataKey}.json`)
.then((r) => {
if (r.data && r.data.data) {
this[sampleDataKey] = r.data.data;
}
})
.catch((error) => {
alert(error.message);
});
},
fetchApi(sampleDataKey) {
// sampleDataKey= clients or history
axios
.get(`api/${sampleDataKey}`)
.then((r) => {
if (r.data) {
this[sampleDataKey] = r.data;
}
})
.catch((error) => {
alert(error.message);
});
},
},
});

View File

@ -0,0 +1,79 @@
import { defineStore } from 'pinia';
import axios from 'axios';
interface Person {
id: number;
name: string;
email: string;
datasetCount: string;
created_at: string;
}
interface TransactionItem {
amount: number;
account: string;
name: string;
date: string;
type: string;
business: string;
}
export const MainService = defineStore('main', {
state: () => ({
/* User */
userName: '',
userEmail: null,
userAvatar: null,
/* Field focus with ctrl+k (to register only once) */
isFieldFocusRegistered: false,
/* Sample data for starting dashboard(commonly used) */
clients: [],
history: [] as Array<TransactionItem>,
authors: [] as Array<Person>,
datasets: [],
}),
actions: {
// payload = authenticated user
setUser(payload) {
if (payload.name) {
this.userName = payload.name;
}
if (payload.email) {
this.userEmail = payload.email;
}
if (payload.avatar) {
this.userAvatar = payload.avatar;
}
},
fetch(sampleDataKey) {
// sampleDataKey= clients or history
axios
.get(`data-sources/${sampleDataKey}.json`)
.then((r) => {
if (r.data && r.data.data) {
this[sampleDataKey] = r.data.data;
}
})
.catch((error) => {
alert(error.message);
});
},
fetchApi(sampleDataKey) {
// sampleDataKey= authors or datasets
axios
.get(`api/${sampleDataKey}`)
.then((r) => {
if (r.data) {
this[sampleDataKey] = r.data;
}
})
.catch((error) => {
alert(error.message);
});
},
},
});

View File

@ -3,11 +3,11 @@ import { createApp, h } from 'vue';
import { Inertia } from '@inertiajs/inertia'; import { Inertia } from '@inertiajs/inertia';
import { createInertiaApp, Link, usePage } from '@inertiajs/vue3'; import { createInertiaApp, Link, usePage } from '@inertiajs/vue3';
import DefaultLayout from '@/Layouts/Default.vue'; // import DefaultLayout from '@/Layouts/Default.vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { StyleService } from '@/Stores/style.js'; import { StyleService } from '@/Stores/style';
import { LayoutService } from '@/Stores/layout.js'; import { LayoutService } from '@/Stores/layout';
import { MainService } from '@/Stores/main'; import { MainService } from '@/Stores/main';
import { darkModeKey, styleKey } from '@/config'; import { darkModeKey, styleKey } from '@/config';
const pinia = createPinia(); const pinia = createPinia();
@ -15,41 +15,36 @@ const pinia = createPinia();
import { initRoutes } from '@eidellev/adonis-stardust/client'; import { initRoutes } from '@eidellev/adonis-stardust/client';
initRoutes(); initRoutes();
// import { defineProps } from 'vue'; // import '@fontsource/archivo-black/index.css';
// import '@fontsource/inter/index.css';
// const props = defineProps({
// user: {
// type: Object,
// default: () => ({}),
// },
// });
createInertiaApp({ createInertiaApp({
progress: { progress: {
color: '#4B5563', // color: '#4B5563',
}, color: '#22C55E',
// resolve: (name) => { },
// const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) // resolve: (name) => {
// return pages[`./Pages/${name}.vue`] // const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
// }, // return pages[`./Pages/${name}.vue`]
// Webpack // },
// resolve: (name) => require(`./Pages/${name}`), // Webpack
// resolve: (name) => require(`./Pages/${name}.vue`), // resolve: (name) => require(`./Pages/${name}`),
// add default layout // resolve: (name) => require(`./Pages/${name}.vue`),
resolve: (name) => { // add default layout
const page = require(`./Pages/${name}.vue`).default; resolve: (name) => {
// if (!page.layout) { const page = require(`./Pages/${name}.vue`).default;
// page.layout = DefaultLayout; // if (!page.layout) {
// } // page.layout = DefaultLayout;
return page; // }
}, return page;
setup({ el, App, props, plugin }) { },
createApp({ render: () => h(App, props) }) setup({ el, App, props, plugin }) {
.use(plugin) createApp({ render: () => h(App, props) })
.use(pinia) .use(plugin)
// .component('inertia-link', Link) .use(pinia)
.mount(el); // .component('inertia-link', Link)
}, .mount(el);
},
}); });
const styleService = StyleService(pinia); const styleService = StyleService(pinia);
@ -62,14 +57,14 @@ styleService.setStyle(localStorage[styleKey] ?? 'basic');
/* Dark mode */ /* Dark mode */
if ( if (
(!localStorage[darkModeKey] && window.matchMedia('(prefers-color-scheme: dark)').matches) || (!localStorage[darkModeKey] && window.matchMedia('(prefers-color-scheme: dark)').matches) ||
localStorage[darkModeKey] === '1' localStorage[darkModeKey] === '1'
) { ) {
styleService.setDarkMode(true); styleService.setDarkMode(true);
} }
/* Collapse mobile aside menu on route change */ /* Collapse mobile aside menu on route change */
Inertia.on('navigate', (event) => { Inertia.on('navigate', (event) => {
layoutService.isAsideMobileExpanded = false; layoutService.isAsideMobileExpanded = false;
layoutService.isAsideLgActive = false; layoutService.isAsideLgActive = false;
}); });

View File

@ -22,6 +22,7 @@ export const colorsText = {
danger: 'text-red-500', danger: 'text-red-500',
warning: 'text-yellow-500', warning: 'text-yellow-500',
info: 'text-blue-500', info: 'text-blue-500',
modern: 'text-teal-500',
}; };
export const colorsOutline = { export const colorsOutline = {
@ -44,6 +45,7 @@ export const getButtonColor = (color, isOutlined, hasHover) => {
danger: 'bg-red-600 dark:bg-red-500 text-white', danger: 'bg-red-600 dark:bg-red-500 text-white',
warning: 'bg-yellow-600 dark:bg-yellow-500 text-white', warning: 'bg-yellow-600 dark:bg-yellow-500 text-white',
info: 'bg-blue-600 dark:bg-blue-500 text-white', info: 'bg-blue-600 dark:bg-blue-500 text-white',
modern: 'bg-teal-600 dark:bg-teal-500 text-white',
}, },
bgHover: { bgHover: {
white: 'hover:bg-gray-50', white: 'hover:bg-gray-50',
@ -56,6 +58,7 @@ export const getButtonColor = (color, isOutlined, hasHover) => {
warning: warning:
'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600', 'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600',
info: 'hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-blue-600 hover:dark:border-blue-600', info: 'hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-blue-600 hover:dark:border-blue-600',
modern: 'hover:bg-emerald-700 hover:border-emerald-700 hover:dark:bg-emerald-600 hover:dark:border-emerald-600',
}, },
borders: { borders: {
white: 'border-gray-100', white: 'border-gray-100',
@ -65,6 +68,7 @@ export const getButtonColor = (color, isOutlined, hasHover) => {
danger: 'border-red-600 dark:border-red-500', danger: 'border-red-600 dark:border-red-500',
warning: 'border-yellow-600 dark:border-yellow-500', warning: 'border-yellow-600 dark:border-yellow-500',
info: 'border-blue-600 dark:border-blue-500', info: 'border-blue-600 dark:border-blue-500',
modern: 'border-teal-600 dark:border-teal-500',
}, },
text: { text: {
white: 'text-black dark:text-slate-100', white: 'text-black dark:text-slate-100',
@ -74,12 +78,13 @@ export const getButtonColor = (color, isOutlined, hasHover) => {
danger: 'text-red-600 dark:text-red-500', danger: 'text-red-600 dark:text-red-500',
warning: 'text-yellow-600 dark:text-yellow-500', warning: 'text-yellow-600 dark:text-yellow-500',
info: 'text-blue-600 dark:text-blue-500', info: 'text-blue-600 dark:text-blue-500',
modern: 'text-teal-600 dark:text-teal-500',
}, },
outlineHover: { outlineHover: {
white: 'hover:bg-gray-100 hover:text-gray-900 dark:hover:text-slate-900', white: 'hover:bg-gray-100 hover:text-gray-900 dark:hover:text-slate-900',
contrast: contrast:
'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black', 'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black',
light: 'hover:bg-gray-100 hover:text-gray-900 dark:hover:text-slate-900', light: 'hover:bg-gray-100 hover:text-gray-900 dark:hover:text-slate-900',
success: success:
'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-emerald-600', 'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-emerald-600',
danger: danger:
@ -87,6 +92,7 @@ export const getButtonColor = (color, isOutlined, hasHover) => {
warning: warning:
'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600', 'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600',
info: 'hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-blue-600', info: 'hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-blue-600',
modern: 'hover:bg-teal-600 hover:text-teal hover:dark:bg-teal-100 hover:dark:text-black',
}, },
}; };

View File

@ -1,43 +0,0 @@
import {
mdiAccountCircle,
mdiMonitor,
mdiGithub,
mdiAccountKey,
mdiAccountEye,
mdiAccountGroup,
mdiPalette,
} from '@mdi/js'
export default [
{
route: 'dashboard',
icon: mdiMonitor,
label: 'Dashboard'
},
// {
// route: 'permission.index',
// icon: mdiAccountKey,
// label: 'Permissions'
// },
// {
// route: 'role.index',
// icon: mdiAccountEye,
// label: 'Roles'
// },
{
route: 'user.index',
icon: mdiAccountGroup,
label: 'Users'
},
{
route: 'role.index',
icon: mdiAccountEye,
label: 'Roles'
},
{
href: 'https://gitea.geologie.ac.at/geolba/tethys',
icon: mdiGithub,
label: 'Gitea',
target: '_blank'
}
]

46
resources/js/menu.ts Normal file
View File

@ -0,0 +1,46 @@
import {
mdiMonitor,
mdiGithub,
mdiAccountEye,
mdiAccountGroup,
mdiDatabasePlus,
} from '@mdi/js';
export default [
{
route: 'dashboard',
icon: mdiMonitor,
label: 'Dashboard',
},
// {
// route: 'permission.index',
// icon: mdiAccountKey,
// label: 'Permissions'
// },
// {
// route: 'role.index',
// icon: mdiAccountEye,
// label: 'Roles'
// },
{
route: 'user.index',
icon: mdiAccountGroup,
label: 'Users',
},
{
route: 'role.index',
icon: mdiAccountEye,
label: 'Roles',
},
{
route: 'dataset.create',
icon: mdiDatabasePlus,
label: 'Create Dataset',
},
{
href: 'https://gitea.geologie.ac.at/geolba/tethys',
icon: mdiGithub,
label: 'Gitea',
target: '_blank',
},
];

View File

@ -10,10 +10,10 @@
| |
*/ */
import 'reflect-metadata' import 'reflect-metadata';
import sourceMapSupport from 'source-map-support' import sourceMapSupport from 'source-map-support';
import { Ignitor } from '@adonisjs/core/build/standalone' import { Ignitor } from '@adonisjs/core/build/standalone';
sourceMapSupport.install({ handleUncaughtExceptions: false }) sourceMapSupport.install({ handleUncaughtExceptions: false });
new Ignitor(__dirname).httpServer().start() new Ignitor(__dirname).httpServer().start();

View File

@ -123,3 +123,23 @@ Route.post('/edit-account-info/store/:id', 'UsersController.accountInfoStore')
.namespace('App/Controllers/Http/Admin') .namespace('App/Controllers/Http/Admin')
.middleware(['auth']); .middleware(['auth']);
// Route::post('change-password', 'UserController@changePasswordStore')->name('admin.account.password.store'); // Route::post('change-password', 'UserController@changePasswordStore')->name('admin.account.password.store');
Route.group(() => {
// Route.get('/user', 'UsersController.index').as('user.index');
Route.get('/dataset/create', 'DatasetController.create').as('dataset.create').middleware(['auth', 'can:dataset-submit']);
Route.post('/dataset/first/first-step', 'DatasetController.firstStep')
.as('dataset.first.step')
.middleware(['auth', 'can:dataset-submit']);
Route.post('/dataset/second/second-step', 'DatasetController.secondStep')
.as('dataset.second.step')
.middleware(['auth', 'can:dataset-submit']);
// Route.get('/user/:id', 'UsersController.show').as('user.show').where('id', Route.matchers.number());
// Route.get('/user/:id/edit', 'UsersController.edit').as('user.edit').where('id', Route.matchers.number());
// Route.put('/user/:id/update', 'UsersController.update').as('user.update').where('id', Route.matchers.number());
// Route.delete('/user/:id', 'UsersController.destroy').as('user.destroy').where('id', Route.matchers.number());
// Route.resource('user', 'DatasetController');
})
.namespace('App/Controllers/Http/Submitter')
.prefix('submitter');
// .middleware(['auth', 'can:dataset-list,dataset-publish']);
// .middleware(['auth', 'is:submitter']);

View File

@ -8,6 +8,7 @@ Route.group(() => {
Route.group(() => { Route.group(() => {
Route.get('authors', 'AuthorsController.index').as('author.index'); Route.get('authors', 'AuthorsController.index').as('author.index');
Route.get('datasets', 'DatasetController.index').as('dataset.index'); Route.get('datasets', 'DatasetController.index').as('dataset.index');
Route.get('persons', 'AuthorsController.persons').as('author.persons');
// Route.get("author/:id", "TodosController.show"); // Route.get("author/:id", "TodosController.show");
// Route.put("author/update", "TodosController.update"); // Route.put("author/update", "TodosController.update");
// Route.post("author", "TodosController.store"); // Route.post("author", "TodosController.store");

View File

@ -1,77 +1,86 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
const plugin = require("tailwindcss/plugin"); const plugin = require('tailwindcss/plugin');
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = { module.exports = {
content: ['./resources/**/*.{edge,js,ts,jsx,tsx,vue}'], content: ['./resources/**/*.{edge,js,ts,jsx,tsx,vue}'],
darkMode: 'class', // or 'media' or 'class' darkMode: 'class', // or 'media' or 'class'
theme: { theme: {
asideScrollbars: { asideScrollbars: {
light: 'light', light: 'light',
gray: 'gray', gray: 'gray',
}, },
extend: { extend: {
zIndex: { colors: {
'-1': '-1', 'primary': '#22C55E',
}, 'primary-dark': '#DCFCE7',
flexGrow: { },
5: '5', fontFamily: {
}, sans: ['Inter', ...defaultTheme.fontFamily.sans],
maxHeight: { logo: ['Archivo Black', ...defaultTheme.fontFamily.sans],
'screen-menu': 'calc(100vh - 3.5rem)', },
'modal': 'calc(100vh - 160px)', zIndex: {
}, '-1': '-1',
transitionProperty: { },
position: 'right, left, top, bottom, margin, padding', flexGrow: {
textColor: 'color', 5: '5',
}, },
keyframes: { maxHeight: {
'fade-out': { 'screen-menu': 'calc(100vh - 3.5rem)',
from: { opacity: 1 }, 'modal': 'calc(100vh - 160px)',
to: { opacity: 0 }, },
}, transitionProperty: {
'fade-in': { position: 'right, left, top, bottom, margin, padding',
from: { opacity: 0 }, textColor: 'color',
to: { opacity: 1 }, },
}, keyframes: {
}, 'fade-out': {
animation: { from: { opacity: 1 },
'fade-out': 'fade-out 250ms ease-in-out', to: { opacity: 0 },
'fade-in': 'fade-in 250ms ease-in-out', },
}, 'fade-in': {
}, //extend from: { opacity: 0 },
}, // theme to: { opacity: 1 },
plugins: [ },
require("@tailwindcss/forms"), },
plugin(function ({ matchUtilities, theme }) { animation: {
matchUtilities( 'fade-out': 'fade-out 250ms ease-in-out',
{ 'fade-in': 'fade-in 250ms ease-in-out',
"aside-scrollbars": (value) => { },
const track = value === "light" ? "100" : "900"; }, //extend
const thumb = value === "light" ? "300" : "600"; }, // theme
const color = value === "light" ? "gray" : value; plugins: [
require('@tailwindcss/forms'),
return { plugin(function ({ matchUtilities, theme }) {
scrollbarWidth: "thin", matchUtilities(
scrollbarColor: `${theme(`colors.${color}.${thumb}`)} ${theme( {
`colors.${color}.${track}` 'aside-scrollbars': (value) => {
)}`, const track = value === 'light' ? '100' : '900';
"&::-webkit-scrollbar": { const thumb = value === 'light' ? '300' : '600';
width: "8px", const color = value === 'light' ? 'gray' : value;
height: "8px",
}, return {
"&::-webkit-scrollbar-track": { 'scrollbarWidth': 'thin',
backgroundColor: theme(`colors.${color}.${track}`), 'scrollbarColor': `${theme(`colors.${color}.${thumb}`)} ${theme(
}, `colors.${color}.${track}`,
"&::-webkit-scrollbar-thumb": { )}`,
borderRadius: "0.25rem", '&::-webkit-scrollbar': {
backgroundColor: theme(`colors.${color}.${thumb}`), width: '8px',
}, height: '8px',
}; },
}, '&::-webkit-scrollbar-track': {
}, backgroundColor: theme(`colors.${color}.${track}`),
{ values: theme("asideScrollbars") } },
); '&::-webkit-scrollbar-thumb': {
}), borderRadius: '0.25rem',
require("@tailwindcss/line-clamp"), backgroundColor: theme(`colors.${color}.${thumb}`),
], },
};
},
},
{ values: theme('asideScrollbars') },
);
}),
require('@tailwindcss/line-clamp'),
],
}; };

View File

@ -1,9 +1,12 @@
{ {
"extends": "adonis-preset-ts/tsconfig.json", "extends": "adonis-preset-ts/tsconfig.json",
"include": [ "include": [
"**/*" // "**/*",
"**/*",
// "./resources/js/**/*" // "./resources/js/**/*"
// "./resources/js/**/*.vue", // "./resources/js/**/*.vue",
// "./resources/js/**/*.ts",
// "./resources/js/**/*.vue",
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",