Skip to content

Commit

Permalink
Nova 4
Browse files Browse the repository at this point in the history
  • Loading branch information
gldrenthe89 committed Sep 22, 2022
1 parent 79b6b92 commit 51cc5b0
Show file tree
Hide file tree
Showing 19 changed files with 318 additions and 218 deletions.
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
/composer.lock
/.idea
/vendor
/package-lock.json
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db
9 changes: 4 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
"role": "Developer"
}
],
"repositories":[
"repositories": [
{
"type": "composer",
"url": "https://nova.laravel.com"
}
],
"require": {
"php": ">=7.1.0",
"laravel/nova": "~3.0",
"illuminate/support": "^7.0|^8.0|^9.0",
"optimistdigital/nova-translations-loader": ">=3.1.2"
"laravel/nova": "~4.0",
"illuminate/support": "^9.0",
"optimistdigital/nova-translations-loader": "^5.0"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion dist/css/field.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion dist/js/field.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions dist/js/field.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*!
* vuex v4.0.2
* (c) 2021 Evan You
* @license MIT
*/
4 changes: 4 additions & 0 deletions dist/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"/js/field.js": "/js/field.js",
"/css/field.css": "/css/field.css"
}
40 changes: 40 additions & 0 deletions nova.mix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')

class NovaExtension {
name() {
return 'nova-extension'
}

register(name) {
this.name = name
}

webpackPlugins() {
return new webpack.ProvidePlugin({
_: 'lodash',
Errors: 'form-backend-validation',
})
}

webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}

webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}

webpackConfig.output = {
uniqueName: this.name,
}
}
}

mix.extend('nova', new NovaExtension())
40 changes: 22 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"cross-env": "^5.0.0",
"laravel-mix": "^1.0",
"laravel-nova": "^1.0"
},
"dependencies": {
"vue": "^2.5.0"
}
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"form-backend-validation": "^2.3.3",
"laravel-mix": "^6.0.41",
"lodash": "^4.17.21",
"postcss": "^8.3.11",
"resolve-url-loader": "^5.0.0",
"sass": "^1.54.9",
"sass-loader": "^12.6.0",
"vue-loader": "^16.8.3"
}
}
1 change: 1 addition & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
15 changes: 8 additions & 7 deletions resources/js/components/Detail/PasswordGeneratorField.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<template>
<panel-item :field="field">
<p slot="value" class="text-90">
&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;
</p>
</panel-item>
<PanelItem :index="index" :field="field">
<template #value>
<p class="text-90">
&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;
</p>
</template>
</PanelItem>
</template>

<script>
export default {
props: ['resource', 'resourceName', 'resourceId', 'field'],
props: ['index', 'resource', 'resourceName', 'resourceId', 'field'],
}
</script>

4 changes: 2 additions & 2 deletions resources/js/components/Detail/StringGeneratorField.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<panel-item :field="field" />
<PanelItem :index="index" :field="field" />
</template>

<script>
export default {
props: ['resource', 'resourceName', 'resourceId', 'field'],
props: ['index', 'resource', 'resourceName', 'resourceId', 'field'],
}
</script>
199 changes: 104 additions & 95 deletions resources/js/components/Form/PasswordGeneratorField.vue
Original file line number Diff line number Diff line change
@@ -1,109 +1,118 @@
<template>
<default-field :field="field" :errors="errors" :show-help-text="showHelpText">
<template slot="field">
<div class="flex">
<input
:id="field.attribute"
:dusk="field.attribute"
:type="passwordFieldType"
v-model="value"
class="w-full form-control form-input form-input-bordered"
:class="errorClasses"
:placeholder="field.name"
autocomplete="new-password"
:disabled="isReadonly"
/>
<div class="my-auto ml-3 cursor-pointer" v-on:click="switchVisibility();">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" :id="field.attribute.concat('VisibilityButton')" fill="#bacad6"><path d="M15 12c0 1.654-1.346 3-3 3s-3-1.346-3-3 1.346-3 3-3 3 1.346 3 3zm9-.449s-4.252 8.449-11.985 8.449c-7.18 0-12.015-8.449-12.015-8.449s4.446-7.551 12.015-7.551c7.694 0 11.985 7.551 11.985 7.551zm-7 .449c0-2.757-2.243-5-5-5s-5 2.243-5 5 2.243 5 5 5 5-2.243 5-5z"/></svg>
</div>
<input v-if="!isReadonly" type="button" class="btn btn-default btn-primary ml-3 cursor-pointer" v-bind:value="__('Generate')" :id="field.attribute.concat('GenerateButton')" v-on:click="generatePassword();">
<input type="button" class="btn btn-default btn-icon ml-3 cursor-pointer" v-bind:value="__('Copy')" :id="field.attribute.concat('CopyButton')" :disabled="!copyEnabled()" v-on:click="copyPassword();">
</div>
</template>
</default-field>
<DefaultField
:field="currentField"
:errors="errors"
:show-help-text="showHelpText"
>
<template #field>
<div class="flex">
<input
:id="currentField.uniqueKey"
:dusk="field.attribute"
type="password"
v-model="value"
class="w-full form-control form-input form-input-bordered"
:class="errorClasses"
:placeholder="field.name"
autocomplete="new-password"
:disabled="currentlyIsReadonly"
/>

<div class="ml-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900" v-on:click="switchVisibility();">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" :id="currentField.uniqueKey.concat('VisibilityButton')"><path d="M15 12c0 1.654-1.346 3-3 3s-3-1.346-3-3 1.346-3 3-3 3 1.346 3 3zm9-.449s-4.252 8.449-11.985 8.449c-7.18 0-12.015-8.449-12.015-8.449s4.446-7.551 12.015-7.551c7.694 0 11.985 7.551 11.985 7.551zm-7 .449c0-2.757-2.243-5-5-5s-5 2.243-5 5 2.243 5 5 5 5-2.243 5-5z"/></svg>
</div>

<input v-if="!isReadonly" type="button" class="ml-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900" v-bind:value="__('Generate')" :id="currentField.uniqueKey.concat('GenerateButton')" v-on:click="generatePassword();">
<input type="button" class="ml-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 text-white dark:text-gray-900" v-bind:value="__('Copy')" :id="currentField.uniqueKey.concat('CopyButton')" :disabled="!copyEnabled()" v-on:click="copyPassword();">
</div>
</template>
</DefaultField>
</template>

<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [HandlesValidationErrors, FormField],
data() {
return {
passwordFieldType: 'password'
}
},
mixins: [HandlesValidationErrors, DependentFormField],
methods: {
copyEnabled() {
return window.location.protocol === 'https:' && this.value.length > 0;
},
generatePassword() {
let chars = '';
if (this.field.exclude_rules && this.field.exclude_rules.length > 0){
for (let j = 0; j < this.field.exclude_rules.length; j++){
this.field.exclude_rules[j] = this.field.exclude_rules[j].toLowerCase();
}
if (this.field.exclude_rules.includes('symbols')
&& this.field.exclude_rules.includes('numbers')
&& this.field.exclude_rules.includes('uppercase')
&& this.field.exclude_rules.includes('lowercase')){
alert(this.__('Include at least one characters type! Symbols, Numbers, Lowercase, Uppercase'));
return false;
data() {
return {
passwordFieldType: 'password'
}
},
if (!this.field.exclude_rules.includes('symbols')){
chars += '!@#$%^&*()-+<>'
}
if (!this.field.exclude_rules.includes('numbers')){
chars += '1234567890'
}
if (!this.field.exclude_rules.includes('uppercase')){
chars += 'ABCDEFGHIJKLMNOP'
}
if (!this.field.exclude_rules.includes('lowercase')){
chars += 'abcdefghijklmnopqrstuvwxyz'
}
} else{
chars = 'abcdefghijklmnopqrstuvwxyz!@#$%^&*()-+<>1234567890ABCDEFGHIJKLMNOP';
}
methods: {
copyEnabled() {
return window.location.protocol === 'https:' && this.value.length > 0;
},
generatePassword() {
let chars = '';
if (this.field.exclude_rules && this.field.exclude_rules.length > 0){
for (let j = 0; j < this.field.exclude_rules.length; j++){
this.field.exclude_rules[j] = this.field.exclude_rules[j].toLowerCase();
}
if (this.field.exclude_rules.includes('symbols')
&& this.field.exclude_rules.includes('numbers')
&& this.field.exclude_rules.includes('uppercase')
&& this.field.exclude_rules.includes('lowercase')){
alert(this.__('Include at least one characters type! Symbols, Numbers, Lowercase, Uppercase'));
return false;
}
let pass = "";
let length = 10;
if (this.field.length > 0){
length = this.field.length;
}
for (let x = 0; x < length; x++) {
const i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
this.value = pass;
if (!this.field.exclude_rules.includes('symbols')){
chars += '!@#$%^&*()-+<>'
}
if (!this.field.exclude_rules.includes('numbers')){
chars += '1234567890'
}
if (!this.field.exclude_rules.includes('uppercase')){
chars += 'ABCDEFGHIJKLMNOP'
}
if (!this.field.exclude_rules.includes('lowercase')){
chars += 'abcdefghijklmnopqrstuvwxyz'
}
} else{
chars = 'abcdefghijklmnopqrstuvwxyz!@#$%^&*()-+<>1234567890ABCDEFGHIJKLMNOP';
}
let button = document.getElementById(this.field.attribute.concat('GenerateButton'));
button.value = this.__('Generated');
setTimeout(() => {
button.value = this.__('Regenerate');
}, 750);
},
copyPassword() {
let fieldText = document.getElementById(this.field.attribute);
let button = document.getElementById(this.field.attribute.concat('CopyButton'));
if (fieldText.value.length > 0) {
navigator.clipboard.writeText(fieldText.value);
button.value = this.__('Copied');
setTimeout(() => {
button.value = this.__('Copy');
}, 750);
}
},
switchVisibility() {
let button = document.getElementById(this.field.attribute.concat('VisibilityButton'));
this.passwordFieldType = this.passwordFieldType === 'password' ? 'text' : 'password';
button.innerHTML = this.passwordFieldType === 'password'
? '<path d="M15 12c0 1.654-1.346 3-3 3s-3-1.346-3-3 1.346-3 3-3 3 1.346 3 3zm9-.449s-4.252 8.449-11.985 8.449c-7.18 0-12.015-8.449-12.015-8.449s4.446-7.551 12.015-7.551c7.694 0 11.985 7.551 11.985 7.551zm-7 .449c0-2.757-2.243-5-5-5s-5 2.243-5 5 2.243 5 5 5 5-2.243 5-5z"/>'
: '<path d="M11.885 14.988l3.104-3.098.011.11c0 1.654-1.346 3-3 3l-.115-.012zm8.048-8.032l-3.274 3.268c.212.554.341 1.149.341 1.776 0 2.757-2.243 5-5 5-.631 0-1.229-.13-1.785-.344l-2.377 2.372c1.276.588 2.671.972 4.177.972 7.733 0 11.985-8.449 11.985-8.449s-1.415-2.478-4.067-4.595zm1.431-3.536l-18.619 18.58-1.382-1.422 3.455-3.447c-3.022-2.45-4.818-5.58-4.818-5.58s4.446-7.551 12.015-7.551c1.825 0 3.456.426 4.886 1.075l3.081-3.075 1.382 1.42zm-13.751 10.922l1.519-1.515c-.077-.264-.132-.538-.132-.827 0-1.654 1.346-3 3-3 .291 0 .567.055.833.134l1.518-1.515c-.704-.382-1.496-.619-2.351-.619-2.757 0-5 2.243-5 5 0 .852.235 1.641.613 2.342z"/>';
let pass = "";
let length = 10;
if (this.field.length > 0){
length = this.field.length;
}
for (let x = 0; x < length; x++) {
const i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
this.value = pass;
let button = document.getElementById(this.currentField.uniqueKey.concat('GenerateButton'));
button.value = this.__('Generated');
setTimeout(() => {
button.value = this.__('Regenerate');
this.copyPassword()
}, 750);
},
copyPassword() {
let fieldText = document.getElementById(this.currentField.uniqueKey);
let button = document.getElementById(this.currentField.uniqueKey.concat('CopyButton'));
if (fieldText.value.length > 0) {
navigator.clipboard.writeText(fieldText.value);
button.value = this.__('Copied');
setTimeout(() => {
button.value = this.__('Copy');
}, 750);
}
},
switchVisibility() {
let field = document.getElementById(this.currentField.uniqueKey);
let button = document.getElementById(this.currentField.uniqueKey.concat('VisibilityButton'));
this.passwordFieldType = this.passwordFieldType === 'password' ? 'text' : 'password';
field.type = this.passwordFieldType;
button.innerHTML = this.passwordFieldType === 'password'
? '<path d="M15 12c0 1.654-1.346 3-3 3s-3-1.346-3-3 1.346-3 3-3 3 1.346 3 3zm9-.449s-4.252 8.449-11.985 8.449c-7.18 0-12.015-8.449-12.015-8.449s4.446-7.551 12.015-7.551c7.694 0 11.985 7.551 11.985 7.551zm-7 .449c0-2.757-2.243-5-5-5s-5 2.243-5 5 2.243 5 5 5 5-2.243 5-5z"/>'
: '<path d="M11.885 14.988l3.104-3.098.011.11c0 1.654-1.346 3-3 3l-.115-.012zm8.048-8.032l-3.274 3.268c.212.554.341 1.149.341 1.776 0 2.757-2.243 5-5 5-.631 0-1.229-.13-1.785-.344l-2.377 2.372c1.276.588 2.671.972 4.177.972 7.733 0 11.985-8.449 11.985-8.449s-1.415-2.478-4.067-4.595zm1.431-3.536l-18.619 18.58-1.382-1.422 3.455-3.447c-3.022-2.45-4.818-5.58-4.818-5.58s4.446-7.551 12.015-7.551c1.825 0 3.456.426 4.886 1.075l3.081-3.075 1.382 1.42zm-13.751 10.922l1.519-1.515c-.077-.264-.132-.538-.132-.827 0-1.654 1.346-3 3-3 .291 0 .567.055.833.134l1.518-1.515c-.704-.382-1.496-.619-2.351-.619-2.757 0-5 2.243-5 5 0 .852.235 1.641.613 2.342z"/>';
}
}
}
}
</script>
Loading

0 comments on commit 51cc5b0

Please sign in to comment.