From 74055ea11b28f6a654c6ac0f83e93ff6dff0fab3 Mon Sep 17 00:00:00 2001 From: Brian Rogers Date: Wed, 30 Mar 2022 13:23:25 -0600 Subject: [PATCH] updating env example file --- scripts/bootstrap.sh | 2 +- src/.env.example | 14 +- src/app/Http/Requests/Users/StoreRequest.php | 43 +++ src/app/Models/User.php | 36 ++- src/app/Support/Address.php | 303 ++++++++++++++++++ src/composer.json | 18 ++ ...00_00_00_000000_create_addresses_table.php | 5 +- src/helpers/constants/cache_ttl.php | 4 + src/helpers/global_functions.php | 126 +++++++- src/resources/css/components/cards.css | 2 +- src/resources/js/Components/DropdownMenu.vue | 10 +- .../Notifications/ErrorNotifications.vue | 50 +++ .../Notifications/GenericNotifications.vue | 48 +++ .../Notifications/SuccessNotifications.vue | 48 +++ .../Notifications/WarningNotifications.vue | 48 +++ .../js/Components/ResponsiveNavLink.vue | 6 +- src/resources/js/Components/SidenavLink.vue | 12 +- src/resources/js/Layouts/AppLayout.vue | 47 ++- src/resources/js/Pages/Dashboard.vue | 50 ++- src/webpack.mix.js | 55 ++++ 20 files changed, 850 insertions(+), 77 deletions(-) create mode 100644 src/app/Http/Requests/Users/StoreRequest.php create mode 100644 src/app/Support/Address.php create mode 100644 src/composer.json create mode 100644 src/resources/js/Components/Notifications/WarningNotifications.vue create mode 100644 src/webpack.mix.js diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 1e6519c..5e61ebf 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -composer require itsgoingd/clockwork enlightn/security-checker +composer require --dev itsgoingd/clockwork enlightn/security-checker diff --git a/src/.env.example b/src/.env.example index e9c6c72..1bb6861 100644 --- a/src/.env.example +++ b/src/.env.example @@ -1,10 +1,16 @@ -APP_NAME=Invoicer +APP_NAME="App Name" APP_ENV=local APP_KEY=base64:hSCTwZ507IdKQ5QJHJ+mQw0DSMgDdAspasjwHCdiB8Y= APP_DEBUG=true -APP_URL=https://invoicer.test +APP_DOMAIN=localhost +APP_URL="https://${APP_DOMAIN}" APP_UID_BYTES=8 +APP_JS_INTEGRITY_HASH="" +GIT_HASH="00000000" +GIT_TAG="x.x.x" +GIT_BRANCH="master" + LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug @@ -12,7 +18,7 @@ LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=mariadb DB_PORT=3306 -DB_DATABASE=invoicer_v3 +DB_DATABASE=database_name DB_USERNAME=root DB_PASSWORD=root @@ -36,7 +42,7 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=null -MAIL_FROM_NAME="${APP_NAME}" +MAIL_FROM_NAME="no-reply@${APP_DOMAIN}" AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= diff --git a/src/app/Http/Requests/Users/StoreRequest.php b/src/app/Http/Requests/Users/StoreRequest.php new file mode 100644 index 0000000..6d40e87 --- /dev/null +++ b/src/app/Http/Requests/Users/StoreRequest.php @@ -0,0 +1,43 @@ +merge(json_decode($this->payload, true, 512, JSON_THROW_ON_ERROR)); + } +} diff --git a/src/app/Models/User.php b/src/app/Models/User.php index b8cfe71..9d3981e 100644 --- a/src/app/Models/User.php +++ b/src/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\Traits\HasUidTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Prunable; use Illuminate\Database\Eloquent\Relations\MorphOne; @@ -11,6 +12,7 @@ use Illuminate\Notifications\Notifiable; use Laravel\Fortify\TwoFactorAuthenticatable; use Laravel\Jetstream\HasProfilePhoto; use Laravel\Jetstream\HasTeams; +use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { @@ -119,28 +121,32 @@ class User extends Authenticatable | */ - /** - * Return the surname then (first)name separated by a comma. - * - * @since 1.0.0 - * - * @return string - */ - public function getFullNameAttribute(): string - { - return "{$this->surname}, {$this->name}"; - } - /** * Return both the (first)name and surname. * * @since 1.0.0 * - * @return string + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getNameFullAttribute(): string + public function fullName(): Attribute { - return "{$this->name} {$this->surname}"; + return Attribute::make( + get: fn ($value, $attributes) => "{$attributes['name']} {$attributes['surname']}", + ); + } + + /** + * Return the surname then (first)name separated by a comma. + * + * @since 1.0.0 + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + public function fullNameReversed(): Attribute + { + return Attribute::make( + get: fn ($value, $attributes) => "{$attributes['surname']}, {$attributes['name']}", + ); } /* diff --git a/src/app/Support/Address.php b/src/app/Support/Address.php new file mode 100644 index 0000000..8f9896d --- /dev/null +++ b/src/app/Support/Address.php @@ -0,0 +1,303 @@ +processAddress($inputs); + } + + /** + * Process user inputs to our Address object. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return void + */ + public function processAddress(array $inputs): void + { + // TODO: attempt to determine how the address is formulated + // could be just numerical indexed, or could be + // named keys but not sure of names + + $this->street = $this->resolveStreet($inputs); + } + + /* + |-------------------------------------------------------------------------- + | Helpers + |-------------------------------------------------------------------------- + | + */ + + /** + * Attempt to determine what the street address + * is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolveStreet(array $inputs): ?string + { + $street = ''; + if (array_key_exists('street', $inputs)) { + $street = $inputs['street']; + } elseif (array_key_exists('street_1', $inputs)) { + $street = $inputs['street_1']; + } elseif (array_key_exists('street_address', $inputs)) { + $street = $inputs['street_address']; + } elseif (array_key_exists('line_1', $inputs)) { + $street = $inputs['line_1']; + } elseif (! empty($inputs[0])) { + $street = $inputs[0]; + } elseif (array_key_exists('0', $inputs)) { + $street = $inputs['0']; + } + + if (empty($street)) { + $street = null; + } + + return $street; + } + + /** + * Attempt to determine what the street address + * unit is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolveUnit(array $inputs): ?string + { + $unit = null; + if (array_key_exists('unit', $inputs)) { + $unit = $inputs['unit']; + } elseif (array_key_exists('street_2', $inputs)) { + $unit = $inputs['street_2']; + } elseif (array_key_exists('line_2', $inputs)) { + $unit = $inputs['line_2']; + } elseif (! empty($inputs[1])) { + $unit = $inputs[1]; + } elseif (array_key_exists('1', $inputs)) { + $unit = $inputs['1']; + } + + if (empty($unit)) { + $unit = null; + } + + return $unit; + } + + /** + * Attempt to determine what the city is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolveCity(array $inputs): ?string + { + $city = ''; + if (array_key_exists('city', $inputs)) { + $city = $inputs['city']; + } elseif (array_key_exists(2, $inputs)) { + $city = $inputs[2]; + } elseif (array_key_exists('2', $inputs)) { + $city = $inputs['2']; + } + + if (empty($city)) { + $city = null; + } + + return $city; + } + + /** + * Attempt to determine what the state is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolveState(array $inputs): ?string + { + $numericKey = 3; + if (empty($this->unit)) { + $numericKey--; + } + + $state = ''; + if (array_key_exists('state', $inputs)) { + $state = $inputs['state']; + } elseif (array_key_exists('st', $inputs)) { + $state = $inputs['st']; + } elseif (array_key_exists($numericKey, $inputs)) { + $state = $inputs[$numericKey]; + } elseif (array_key_exists("$numericKey", $inputs)) { + $state = $inputs["$numericKey"]; + } + + if (empty($state)) { + $state = null; + } + + return $state; + } + + /** + * Attempt to determine what the postal code is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolvePostalCode(array $inputs): ?string + { + $numericKey = 4; + if (empty($this->unit)) { + $numericKey--; + } + + $postalCode = ''; + if (array_key_exists('postal_code', $inputs)) { + $postalCode = $inputs['postal_code']; + } elseif (array_key_exists('postalCode', $inputs)) { + $postalCode = $inputs['postalCode']; + } elseif (array_key_exists('postal', $inputs)) { + $postalCode = $inputs['postal']; + } elseif (array_key_exists('zipcode', $inputs)) { + $postalCode = $inputs['zipcode']; + } elseif (array_key_exists('zip_code', $inputs)) { + $postalCode = $inputs['zip_code']; + } elseif (array_key_exists('zipCode', $inputs)) { + $postalCode = $inputs['zipCode']; + } elseif (array_key_exists('zip', $inputs)) { + $postalCode = $inputs['zip']; + } elseif (array_key_exists($numericKey, $inputs)) { + $postalCode = $inputs[$numericKey]; + } elseif (array_key_exists("$numericKey", $inputs)) { + $postalCode = $inputs["$numericKey"]; + } + + if (empty($postalCode)) { + $postalCode = null; + } + + return $postalCode; + } + + /** + * Attempt to determine what the country is in the given data. + * + * @since 1.0.0 + * + * @param array $inputs + * + * @return string|null + */ + private function resolveCountry(array $inputs): ?string + { + $numericKey = 5; + if (empty($this->unit)) { + $numericKey--; + } + + $country = ''; + if (array_key_exists('country', $inputs)) { + $country = $inputs['country']; + } elseif (array_key_exists('st', $inputs)) { + $country = $inputs['st']; + } elseif (array_key_exists($numericKey, $inputs)) { + $country = $inputs[$numericKey]; + } elseif (array_key_exists("$numericKey", $inputs)) { + $country = $inputs["$numericKey"]; + } + + if (empty($country)) { + $country = null; + } + + return $country; + } + + /** + * Magic method to convert this object to a string. + * + * @since 1.0.0 + * + * @return string + */ + public function __toString(): string + { + $fullAddress = $this->street; + if (! empty($this->unit)) { + $fullAddress .= " {$this->unit}"; + } + + if (! empty($this->city)) { + $fullAddress .= ", {$this->city}"; + } + + if (! empty($this->state)) { + $fullAddress .= ", {$this->state}"; + } + + if (! empty($this->postalCode)) { + $fullAddress .= $this->postalCode; + } + + if (! empty($this->country)) { + $fullAddress .= ", {$this->country}"; + } + + + + return $fullAddress; + } +} diff --git a/src/composer.json b/src/composer.json new file mode 100644 index 0000000..d0c83ba --- /dev/null +++ b/src/composer.json @@ -0,0 +1,18 @@ +{ + //... + "autoload": { + //... + "files": [ + "helpers/constants/cache_ttl.php", + "helpers/constants/http_status_codes.php", + "helpers/global_functions.php" + ] + }, + //... + "scripts": { + "post-autoload-dump": [ + //... + "@php artisan ziggy:generate --ansi" + ], + }, +} \ No newline at end of file diff --git a/src/database/migrations/1000_00_00_000000_create_addresses_table.php b/src/database/migrations/1000_00_00_000000_create_addresses_table.php index cefbe47..cade0f3 100644 --- a/src/database/migrations/1000_00_00_000000_create_addresses_table.php +++ b/src/database/migrations/1000_00_00_000000_create_addresses_table.php @@ -22,12 +22,13 @@ class CreateAddressesTable extends Migration $table->string('state'); $table->string('postal_code'); // leave as string to accomodate 12345-6789 or Canada $table->string('country')->default('United States'); - $table->float('latitude', 12, 9)->index()->nullable(); - $table->float('longitude', 12, 9)->index()->nullable(); + $table->float('latitude', 12, 9)->nullable(); + $table->float('longitude', 12, 9)->nullable(); $table->point('location')->nullable(); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->index(['latitude', 'longitude']); $table->spatialIndex('location'); }); } diff --git a/src/helpers/constants/cache_ttl.php b/src/helpers/constants/cache_ttl.php index a7d0806..638d0ba 100644 --- a/src/helpers/constants/cache_ttl.php +++ b/src/helpers/constants/cache_ttl.php @@ -5,6 +5,10 @@ define('CACHE_TTL_FIFTEEN_MINUTES', 900); define('CACHE_TTL_HALF_HOUR', 1800); define('CACHE_TTL_ONE_HOUR', 3600); +define('CACHE_TTL_TWO_HOURS', 7200); +define('CACHE_TTL_THREE_HOURS', 10800); +define('CACHE_TTL_SIX_HOURS', 21600); +define('CACHE_TTL_TWELVE_HOURS', 43200); define('CACHE_TTL_ONE_DAY', 86400); diff --git a/src/helpers/global_functions.php b/src/helpers/global_functions.php index 8911269..db6096b 100644 --- a/src/helpers/global_functions.php +++ b/src/helpers/global_functions.php @@ -17,7 +17,16 @@ if (! function_exists('snake2Title')) { } if (! function_exists('carbon')) { - function carbon(?string $timestring = null) + /** + * Return a Carbon object based on a given timestring. + * It will attempt to find a timezone in the current + * session but default to UTC. + * + * @param string|null $timestring + * + * @return \Carbon\Carbon + */ + function carbon(?string $timestring = null): \Carbon\Carbon { $carbon = Carbon\Carbon::now(session('timezone_name')); if (! empty($timestring)) { @@ -28,7 +37,15 @@ if (! function_exists('carbon')) { } if (! function_exists('jddayofweek')) { - function jddayofweek(?int $intDay = null, int $mode = 0) + /** + * Returns the day of the week. Can return a string or an integer depending on the mode. + * + * @param int|null $intDay + * @param int $mode + * + * @return string + */ + function jddayofweek(?int $intDay = null, int $mode = 0): string { if (is_null($intDay)) { $intDay = date('l'); @@ -41,3 +58,108 @@ if (! function_exists('jddayofweek')) { return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][$intDay]; } } + +if (! function_exists('cel2Fah')) { + /** + * Convert from celsius to fahrenheit. + * + * @param float|int|string $celsius + * @param int $precision + * + * @return float + */ + function cel2Fah($celsius, int $preceision = 0): float + { + return round((($celsius * (9/5)) + 32), $preceision); + } +} + +if (! function_exists('fah2Cel')) { + /** + * Convert from fahrenheit to celsius. + * + * @param float|int|string $fahrenheit + * @param int $precision + * + * @return float + */ + function fah2Cel($fahrenheit, int $preceision = 1): float + { + return round(($fahrenheit - 32 * (5/9)), $preceision); + } +} + +if (! function_exists('meters2Miles')) { + /** + * Convert from meters to miles. + * + * @param float|int|string $meters + * @param int $precision + * + * @return float + */ + function meters2Miles($meters, int $preceision = 1): float + { + return round(($meters * 0.00062137), $preceision); + } +} + +if (! function_exists('kilometers2Miles')) { + /** + * Convert from kilometers to meters. + * + * @param float|int|string $kilometers + * @param int $precision + * + * @return float + */ + function kilometers2Miles($kilometers, int $preceision = 1): float + { + return round(($kilometers * 1.609), $preceision); + } +} + +if (! function_exists('m2Km')) { + /** + * Convert from meters to kilometers. + * + * @param float|int|string $meters + * @param int $precision + * + * @return float + */ + function m2Km($meters, int $preceision = 1): float + { + return round(($meters / 1000), $preceision); + } +} + +if (! function_exists('mm2Inches')) { + /** + * Convert from milimeters to inches. + * + * @param float|int|string $milimeters + * @param int $precision + * + * @return float + */ + function mm2Inches($milimeters, int $preceision = 1): float + { + return round(($milimeters / 25.4), $preceision); + } +} + +if (! function_exists('pa2Mbar')) { + /** + * Convert from pascals to milibars. + * + * @param float|int|string $pascals + * @param int $precision + * + * @return float + */ + function pa2Mbar($pascals, int $preceision = 1): float + { + return round(($pascals / 100), $preceision); + } +} diff --git a/src/resources/css/components/cards.css b/src/resources/css/components/cards.css index c8ddc3c..1724655 100644 --- a/src/resources/css/components/cards.css +++ b/src/resources/css/components/cards.css @@ -1,5 +1,5 @@ .card { - @apply flex flex-col nm-flat-white dark:nm-flat-gray-700 border border-gray-100 dark:border-gray-800 overflow-hidden rounded-lg; + @apply flex flex-col bg-white dark:bg-zinc-700 border border-gray-100 dark:border-gray-800 overflow-hidden rounded-lg; } .card .card-header { diff --git a/src/resources/js/Components/DropdownMenu.vue b/src/resources/js/Components/DropdownMenu.vue index 88efdf1..44d613a 100644 --- a/src/resources/js/Components/DropdownMenu.vue +++ b/src/resources/js/Components/DropdownMenu.vue @@ -25,7 +25,7 @@ diff --git a/src/resources/js/Components/Notifications/GenericNotifications.vue b/src/resources/js/Components/Notifications/GenericNotifications.vue index e69de29..b0622a6 100644 --- a/src/resources/js/Components/Notifications/GenericNotifications.vue +++ b/src/resources/js/Components/Notifications/GenericNotifications.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/resources/js/Components/Notifications/SuccessNotifications.vue b/src/resources/js/Components/Notifications/SuccessNotifications.vue index e69de29..14adcce 100644 --- a/src/resources/js/Components/Notifications/SuccessNotifications.vue +++ b/src/resources/js/Components/Notifications/SuccessNotifications.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/resources/js/Components/Notifications/WarningNotifications.vue b/src/resources/js/Components/Notifications/WarningNotifications.vue new file mode 100644 index 0000000..fc49aa5 --- /dev/null +++ b/src/resources/js/Components/Notifications/WarningNotifications.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/resources/js/Components/ResponsiveNavLink.vue b/src/resources/js/Components/ResponsiveNavLink.vue index ab5e7a4..9507d96 100644 --- a/src/resources/js/Components/ResponsiveNavLink.vue +++ b/src/resources/js/Components/ResponsiveNavLink.vue @@ -46,7 +46,7 @@ export default defineComponent({ "transition", ] - let tiveClasses = [ + let activeClasses = [ "border-transparent", "text-gray-600", "hover:text-gray-800", @@ -58,7 +58,7 @@ export default defineComponent({ ] if (props.active) { - tiveClasses = [ + activeClasses = [ "border-indigo-400", "text-indigo-700", "bg-indigo-50", @@ -68,7 +68,7 @@ export default defineComponent({ ] } - defaultClasses = dClasses.concat(tiveClasses) + defaultClasses = dClasses.concat(activeClasses) for (let elm of props.class.split(" ")) { elm = elm.trim() diff --git a/src/resources/js/Components/SidenavLink.vue b/src/resources/js/Components/SidenavLink.vue index f5ba5fc..4f78810 100644 --- a/src/resources/js/Components/SidenavLink.vue +++ b/src/resources/js/Components/SidenavLink.vue @@ -44,20 +44,20 @@ export default defineComponent({ "rounded-lg", ] - let tiveClasses = [ - "nm-flat-white", - "hover:nm-concave-blue-300", + let activeClasses = [ + "bg-white", + "hover:bg-blue-300", "hover:text-white", ] if (props.active) { - tiveClasses = [ - "nm-flat-blue-400", + activeClasses = [ + "bg-blue-400", "text-white", ] } - defaultClasses = dClasses.concat(tiveClasses) + defaultClasses = dClasses.concat(activeClasses) for (let elm of props.class.split(" ")) { elm = elm.trim() diff --git a/src/resources/js/Layouts/AppLayout.vue b/src/resources/js/Layouts/AppLayout.vue index 86908d7..ae05ee2 100644 --- a/src/resources/js/Layouts/AppLayout.vue +++ b/src/resources/js/Layouts/AppLayout.vue @@ -3,9 +3,10 @@
- - - + + + +
@@ -206,10 +207,6 @@ Profile - - Billing - - API Tokens @@ -237,17 +234,17 @@ diff --git a/src/webpack.mix.js b/src/webpack.mix.js new file mode 100644 index 0000000..a83650d --- /dev/null +++ b/src/webpack.mix.js @@ -0,0 +1,55 @@ +const mix = require('laravel-mix'); +const { exec } = require('child_process'); + +const shaCmd = "cat public/js/app.js | openssl dgst -sha512 -binary | openssl base64 -A"; + +/* + |-------------------------------------------------------------------------- + | Mix Asset Management + |-------------------------------------------------------------------------- + | + | Mix provides a clean, fluent API for defining some Webpack build steps + | for your Laravel applications. By default, we are compiling the CSS + | file for the application as well as bundling up all the JS files. + | + */ + +mix.js('resources/js/app.js', 'public/js') + .vue() + .sourceMaps() + .after(stats => { + exec(shaCmd, (shaError, shaStdout, shaStderr) => { + if (shaError) { + console.error(`shaCmd error: ${shaError.message}`); + return; + } + if (shaStderr) { + console.error(`shaCmd stderr: ${shaStderr}`); + return; + } + let sha512Hash = shaStdout; + let sedCmd = `sed -i '/^APP_JS_INTEGRITY_HASH="*/c\APP_JS_INTEGRITY_HASH="sha512-${sha512Hash}"' .env`; + + exec(sedCmd, (sedError, sedStdout, sedStderr) => { + if (sedError) { + console.error(`sedCmd error: ${sedError.message}`); + return; + } + if (sedStderr) { + console.error(`sedCmd stderr: ${sedStderr}`); + return; + } + console.log('Replaced SHA512 hash for integrity attribute.'); + }) + }) + }) + .postCss('resources/css/app.css', 'public/css', [ + require('postcss-import'), + require('tailwindcss'), + ]) + .sourceMaps() + .webpackConfig(require('./webpack.config')); + +if (mix.inProduction()) { + mix.version(); +}