Skip to content

Commit

Permalink
Add profile page
Browse files Browse the repository at this point in the history
  • Loading branch information
Kamona-WD committed Dec 29, 2022
1 parent 84dff9b commit cf80c0c
Show file tree
Hide file tree
Showing 40 changed files with 1,949 additions and 20 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ We recommend installing this package on a project that you are starting from scr
> **_note_** It doesn't matter if you use `vitejs` or `laravel-mix`, it will work in both cases.
1. Fresh install Laravel >= 8.0 and `cd` to your app.
2. Install laravel/breeze
2. Install laravel/breeze >= `1.15.0`

```sh
composer require laravel/breeze --dev
Expand All @@ -35,6 +35,9 @@ php artisan breeze:install
```sh
composer require kamona/kui-laravel-breeze --dev

# for laravel/breeze < 1.15.0
composer require kamona/kui-laravel-breeze:0.3.0 --dev

# after finish run this command

# This package will detect if your project uses vitejs or not by check if vite.config.js exist or not.
Expand Down
15 changes: 9 additions & 6 deletions src/Console/ReplaceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,21 @@ protected function replaceBlade()
(new Filesystem)->ensureDirectoryExists(resource_path('views/auth'));
(new Filesystem)->ensureDirectoryExists(resource_path('views/layouts'));
(new Filesystem)->ensureDirectoryExists(resource_path('views/components'));
(new Filesystem)->ensureDirectoryExists(resource_path('views/profile'));
(new Filesystem)->ensureDirectoryExists(resource_path('views/buttons-showcase'));

// Clean directories
(new Filesystem)->cleanDirectory(resource_path('views/auth'));
(new Filesystem)->cleanDirectory(resource_path('views/layouts'));
(new Filesystem)->cleanDirectory(resource_path('views/components'));
(new Filesystem)->cleanDirectory(resource_path('views/profile'));
(new Filesystem)->cleanDirectory(resource_path('views/buttons-showcase'));

copy(__DIR__ . '/../../stubs/blade/views/dashboard.blade.php', resource_path('views/dashboard.blade.php'));

(new Filesystem)->copyDirectory(__DIR__ . '/../../stubs/blade/views/auth', resource_path('views/auth'));
(new Filesystem)->copyDirectory(__DIR__ . '/../../stubs/blade/views/components', resource_path('views/components'));
(new Filesystem)->copyDirectory(__DIR__ . '/../../stubs/blade/views/profile', resource_path('views/profile'));
(new Filesystem)->copyDirectory(__DIR__ . '/../../stubs/blade/views/buttons-showcase', resource_path('views/buttons-showcase'));

if (!$this->isVite) {
Expand All @@ -117,8 +120,8 @@ protected function replaceBlade()
// Icons
$this->requireComposerPackages('blade-ui-kit/blade-heroicons:^1.2');

$this->info('Breeze scaffolding replaced successfully.');
$this->comment('Please execute the "npm install && npm run dev" command to build your assets.');
$this->components->info('Breeze scaffolding replaced successfully.');
$this->components->info('Please execute the "npm install && npm run dev" command to build your assets.');
}

protected function replaceVue($type)
Expand Down Expand Up @@ -181,8 +184,8 @@ protected function replaceVue($type)
copy(__DIR__ . '/../../stubs/vue/vite.config.js', base_path('vite.config.js'));
}

$this->info('Breeze scaffolding replaced successfully.');
$this->comment('Please execute the "npm install && npm run dev" command to build your assets.');
$this->components->info('Breeze scaffolding replaced successfully.');
$this->components->info('Please execute the "npm install && npm run dev" command to build your assets.');
}

protected function replaceReact()
Expand Down Expand Up @@ -232,8 +235,8 @@ protected function replaceReact()
$this->replaceInFile("'resources/js/app.js'", "'resources/js/app.jsx'", resource_path('views/app.blade.php'));
}

$this->info('Breeze scaffolding replaced successfully.');
$this->comment('Please execute the "npm install && npm run dev" command to build your assets.');
$this->components->info('Breeze scaffolding replaced successfully.');
$this->components->info('Please execute the "npm install && npm run dev" command to build your assets.');
}

protected function replaceFavIcon()
Expand Down
2 changes: 1 addition & 1 deletion stubs/blade/views/components/auth-session-status.blade.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@props(['status'])

@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600 dark:text-gray-400']) }}>
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}>
{{ $status }}
</div>
@endif
77 changes: 77 additions & 0 deletions stubs/blade/views/components/modal.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@props([
'name',
'show' => false,
'maxWidth' => '2xl'
])

@php
$maxWidth = [
'sm' => 'sm:max-w-sm',
'md' => 'sm:max-w-md',
'lg' => 'sm:max-w-lg',
'xl' => 'sm:max-w-xl',
'2xl' => 'sm:max-w-2xl',
][$maxWidth];
@endphp

<div
x-data="{
show: @js($show),
focusables() {
// All focusable element types...
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
return [...$el.querySelectorAll(selector)]
// All non-disabled elements...
.filter(el => ! el.hasAttribute('disabled'))
},
firstFocusable() { return this.focusables()[0] },
lastFocusable() { return this.focusables().slice(-1)[0] },
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
}"
x-init="$watch('show', value => {
if (value) {
document.body.classList.add('overflow-y-hidden');
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
} else {
document.body.classList.remove('overflow-y-hidden');
}
})"
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
x-on:close.stop="show = false"
x-on:keydown.escape.window="show = false"
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
x-show="show"
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
style="display: {{ $show ? 'block' : 'none' }};"
>
<div
x-show="show"
class="fixed inset-0 transform transition-all"
x-on:click="show = false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div>
</div>

<div
x-show="show"
class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
{{ $slot }}
</div>
</div>
7 changes: 7 additions & 0 deletions stubs/blade/views/components/navbar.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ class="w-4 h-4 fill-current"
</x-slot>

<x-slot name="content">
<!-- Profile -->
<x-dropdown-link
:href="route('profile.edit')"
>
{{ __('Profile') }}
</x-dropdown-link>

<!-- Authentication -->
<form method="POST" action="{{ route('logout') }}">
@csrf
Expand Down
27 changes: 27 additions & 0 deletions stubs/blade/views/profile/edit.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl leading-tight">
{{ __('Profile') }}
</h2>
</x-slot>

<div class="space-y-6">
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg dark:bg-gray-800">
<div class="max-w-xl">
@include('profile.partials.update-profile-information-form')
</div>
</div>

<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg dark:bg-gray-800">
<div class="max-w-xl">
@include('profile.partials.update-password-form')
</div>
</div>

<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg dark:bg-gray-800">
<div class="max-w-xl">
@include('profile.partials.delete-user-form')
</div>
</div>
</div>
</x-app-layout>
77 changes: 77 additions & 0 deletions stubs/blade/views/profile/partials/delete-user-form.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<section class="space-y-6">
<header>
<h2 class="text-lg font-medium">
{{ __('Delete Account') }}
</h2>

<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }}
</p>
</header>

<x-button
variant="danger"
x-data=""
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')"
>
{{ __('Delete Account') }}
</x-button>

<x-modal
name="confirm-user-deletion"
:show="$errors->userDeletion->isNotEmpty()"
focusable
>
<form
method="post"
action="{{ route('profile.destroy') }}"
class="p-6"
>
@csrf
@method('delete')

<h2 class="text-lg font-medium">
{{ __('Are you sure you want to delete your account?') }}
</h2>

<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }}
</p>

<div class="mt-6 space-y-6">
<x-form.label
for="delete-user-password"
value="Password"
class="sr-only"
/>

<x-form.input
id="delete-user-password"
name="password"
type="password"
class="block w-3/4"
placeholder="Password"
/>

<x-form.error :messages="$errors->userDeletion->get('password')" />
</div>

<div class="mt-6 flex justify-end">
<x-button
type="button"
variant="secondary"
x-on:click="$dispatch('close')"
>
{{ __('Cancel') }}
</x-button>

<x-button
variant="danger"
class="ml-3"
>
{{ __('Delete Account') }}
</x-button>
</div>
</form>
</x-modal>
</section>
89 changes: 89 additions & 0 deletions stubs/blade/views/profile/partials/update-password-form.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<section>
<header>
<h2 class="text-lg font-medium">
{{ __('Update Password') }}
</h2>

<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __('Ensure your account is using a long, random password to stay secure.') }}
</p>
</header>

<form
method="post"
action="{{ route('password.update') }}"
class="mt-6 space-y-6"
>
@csrf
@method('put')

<div class="space-y-2">
<x-form.label
for="current_password"
:value="__('Current Password')"
/>

<x-form.input
id="current_password"
name="current_password"
type="password"
class="block w-full"
autocomplete="current-password"
/>

<x-form.error :messages="$errors->updatePassword->get('current_password')" />
</div>

<div class="space-y-2">
<x-form.label
for="password"
:value="__('New Password')"
/>

<x-form.input
id="password"
name="password"
type="password"
class="block w-full"
autocomplete="new-password"
/>

<x-form.error :messages="$errors->updatePassword->get('password')" />
</div>

<div class="space-y-2">
<x-form.label
for="password_confirmation"
:value="__('Confirm Password')"
/>

<x-form.input
id="password_confirmation"
name="password_confirmation"
type="password"
class="block w-full"
autocomplete="new-password"
/>

<x-form.error :messages="$errors->updatePassword->get('password_confirmation')" />
</div>

<div class="flex items-center gap-4">
<x-button>
{{ __('Save') }}
</x-button>

@if (session('status') === 'password-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600 dark:text-gray-400"
>
{{ __('Saved.') }}
</p>
@endif
</div>
</form>
</section>
Loading

0 comments on commit cf80c0c

Please sign in to comment.