mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-04 07:22:34 +00:00
User management (#185)
This commit is contained in:
61
resources/views/settings/users/index.blade.php
Normal file
61
resources/views/settings/users/index.blade.php
Normal file
@ -0,0 +1,61 @@
|
||||
<x-settings-layout>
|
||||
<x-slot name="pageTitle">Users</x-slot>
|
||||
|
||||
<x-container>
|
||||
<x-card-header>
|
||||
<x-slot name="title">Users</x-slot>
|
||||
<x-slot name="description">Here you can manage users</x-slot>
|
||||
<x-slot name="aside">
|
||||
@include("settings.users.partials.create-user")
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
<div class="space-y-3" x-data="{ deleteAction: '' }">
|
||||
<x-table>
|
||||
<x-thead>
|
||||
<x-tr>
|
||||
<x-th>ID</x-th>
|
||||
<x-th>Name</x-th>
|
||||
<x-th>Email</x-th>
|
||||
<x-th>Role</x-th>
|
||||
<x-th></x-th>
|
||||
</x-tr>
|
||||
</x-thead>
|
||||
<x-tbody>
|
||||
@foreach ($users as $user)
|
||||
<x-tr>
|
||||
<x-td>{{ $user->id }}</x-td>
|
||||
<x-td>{{ $user->name }}</x-td>
|
||||
<x-td>{{ $user->email }}</x-td>
|
||||
<x-td>
|
||||
<div class="inline-flex">
|
||||
@if ($user->role === \App\Enums\UserRole::ADMIN)
|
||||
<x-status status="success">ADMIN</x-status>
|
||||
@else
|
||||
<x-status status="info">USER</x-status>
|
||||
@endif
|
||||
</div>
|
||||
</x-td>
|
||||
<x-td class="text-right">
|
||||
<x-icon-button
|
||||
x-on:click="deleteAction = '{{ route('settings.users.delete', ['user' => $user]) }}'; $dispatch('open-modal', 'delete-user')"
|
||||
>
|
||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||
</x-icon-button>
|
||||
<x-icon-button :href="route('settings.users.show', ['user' => $user])">
|
||||
<x-heroicon name="o-cog-6-tooth" class="h-5 w-5" />
|
||||
</x-icon-button>
|
||||
</x-td>
|
||||
</x-tr>
|
||||
@endforeach
|
||||
</x-tbody>
|
||||
</x-table>
|
||||
<x-confirmation-modal
|
||||
name="delete-user"
|
||||
:title="__('Confirm')"
|
||||
:description="__('Are you sure that you want to delete this user?')"
|
||||
method="delete"
|
||||
x-bind:action="deleteAction"
|
||||
/>
|
||||
</div>
|
||||
</x-container>
|
||||
</x-settings-layout>
|
@ -0,0 +1,69 @@
|
||||
<div>
|
||||
<x-primary-button x-data="" x-on:click.prevent="$dispatch('open-modal', 'create-user')">New User</x-primary-button>
|
||||
|
||||
<x-modal name="create-user">
|
||||
<form
|
||||
id="create-user-form"
|
||||
hx-post="{{ route("settings.users.store") }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#create-user-form"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-create-user"
|
||||
class="p-6"
|
||||
>
|
||||
@csrf
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Create New User</h2>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="name" value="Name" />
|
||||
<x-text-input value="{{ old('name') }}" id="name" name="name" type="text" class="mt-1 w-full" />
|
||||
@error("name")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="email" value="Email" />
|
||||
<x-text-input value="{{ old('email') }}" id="email" name="email" type="text" class="mt-1 w-full" />
|
||||
@error("email")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="password" value="Password" />
|
||||
<x-text-input id="password" name="password" type="password" class="mt-1 w-full" />
|
||||
@error("password")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="role" value="Role" />
|
||||
<x-select-input id="role" name="role" class="mt-1 w-full">
|
||||
<option
|
||||
value="{{ \App\Enums\UserRole::USER }}"
|
||||
@if(old('role') === \App\Enums\UserRole::USER) selected @endif
|
||||
>
|
||||
User
|
||||
</option>
|
||||
<option
|
||||
value="{{ \App\Enums\UserRole::ADMIN }}"
|
||||
@if(old('role') === \App\Enums\UserRole::ADMIN) selected @endif
|
||||
>
|
||||
Admin
|
||||
</option>
|
||||
</x-select-input>
|
||||
@error("role")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">Cancel</x-secondary-button>
|
||||
|
||||
<x-primary-button id="btn-create-project" class="ml-3">Create</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-modal>
|
||||
</div>
|
@ -0,0 +1,120 @@
|
||||
<x-card>
|
||||
<x-slot name="title">Projects</x-slot>
|
||||
|
||||
<x-slot name="description">Manage the projects that the user is in</x-slot>
|
||||
|
||||
<x-slot name="aside">
|
||||
<x-secondary-button :href="route('settings.users.index')">Back to Users</x-secondary-button>
|
||||
</x-slot>
|
||||
|
||||
<form
|
||||
id="update-projects"
|
||||
hx-post="{{ route("settings.users.update-projects", ["user" => $user]) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#update-projects"
|
||||
hx-trigger="submit"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-save-projects"
|
||||
class="mt-6"
|
||||
>
|
||||
@csrf
|
||||
|
||||
<script>
|
||||
let projects = @json($user->projects);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="space-y-6"
|
||||
x-data="{
|
||||
q: '',
|
||||
projects: projects,
|
||||
search() {
|
||||
htmx.ajax('GET', '{{ request()->getUri() }}?q=' + this.q, {
|
||||
target: '#projects-list',
|
||||
swap: 'outerHTML',
|
||||
select: '#projects-list',
|
||||
}).then(() => {
|
||||
document.getElementById('q').focus()
|
||||
})
|
||||
},
|
||||
addProject(project) {
|
||||
if (this.projects.find((p) => p.id === project.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.projects.push(project)
|
||||
this.q = ''
|
||||
},
|
||||
removeProject(id) {
|
||||
this.projects = this.projects.filter((project) => project.id !== id)
|
||||
},
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
<x-input-label value="Projects" />
|
||||
|
||||
<div class="mt-1">
|
||||
<template x-for="project in projects">
|
||||
<div class="mr-1 inline-flex">
|
||||
<x-status status="info" class="flex items-center">
|
||||
<span x-text="project.name"></span>
|
||||
<x-heroicon
|
||||
name="o-x-mark"
|
||||
class="ml-1 h-4 w-4 cursor-pointer"
|
||||
x-on:click="removeProject(project.id)"
|
||||
/>
|
||||
<input type="hidden" name="projects[]" x-bind:value="project.id" />
|
||||
</x-status>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label value="Add new Project" />
|
||||
|
||||
@php
|
||||
$projects = \App\Models\Project::query()
|
||||
->where(function ($query) {
|
||||
if (request()->has("q")) {
|
||||
$query->where("name", "like", "%" . request("q") . "%");
|
||||
}
|
||||
})
|
||||
->take(5)
|
||||
->get();
|
||||
@endphp
|
||||
|
||||
<x-dropdown width="full">
|
||||
<x-slot name="trigger">
|
||||
<x-text-input
|
||||
id="q"
|
||||
name="q"
|
||||
x-model="q"
|
||||
type="text"
|
||||
class="mt-1 w-full"
|
||||
placeholder="Search for projects..."
|
||||
autocomplete="off"
|
||||
x-on:input.debounce.500ms="search"
|
||||
/>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<div id="projects-list">
|
||||
@foreach ($projects as $project)
|
||||
<x-dropdown-link
|
||||
class="cursor-pointer"
|
||||
x-on:click="addProject({ id: {{ $project->id }}, name: '{{ $project->name }}' })"
|
||||
>
|
||||
{{ $project->name }}
|
||||
</x-dropdown-link>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
<x-primary-button id="btn-save-projects" form="update-projects">Save</x-primary-button>
|
||||
</x-slot>
|
||||
</x-card>
|
@ -0,0 +1,99 @@
|
||||
<x-card>
|
||||
<x-slot name="title">User Info</x-slot>
|
||||
|
||||
<x-slot name="description">You can update user's info here</x-slot>
|
||||
|
||||
<form
|
||||
id="update-user-info"
|
||||
hx-post="{{ route("settings.users.update", ["user" => $user]) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#update-user-info"
|
||||
hx-trigger="submit"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-save-info"
|
||||
class="mt-6 space-y-6"
|
||||
>
|
||||
@csrf
|
||||
<div>
|
||||
<x-input-label for="name" value="Name" />
|
||||
<x-text-input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
value="{{ old('name', $user->name) }}"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="name"
|
||||
/>
|
||||
@error("name")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="email" value="Email" />
|
||||
<x-text-input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
value="{{ old('email', $user->email) }}"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="email"
|
||||
/>
|
||||
@error("email")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="timezone" value="Timezone" />
|
||||
<x-select-input id="timezone" name="timezone" class="mt-1 block w-full" required>
|
||||
@foreach (timezone_identifiers_list() as $timezone)
|
||||
<option
|
||||
value="{{ $timezone }}"
|
||||
@if(old('timezone', $user->timezone) == $timezone) selected @endif
|
||||
>
|
||||
{{ $timezone }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error("timezone")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="role" value="Role" />
|
||||
<x-select-input id="role" name="role" class="mt-1 w-full">
|
||||
<option
|
||||
value="{{ \App\Enums\UserRole::USER }}"
|
||||
@if(old('role', $user->role) === \App\Enums\UserRole::USER) selected @endif
|
||||
>
|
||||
User
|
||||
</option>
|
||||
<option
|
||||
value="{{ \App\Enums\UserRole::ADMIN }}"
|
||||
@if(old('role', $user->role) === \App\Enums\UserRole::ADMIN) selected @endif
|
||||
>
|
||||
Admin
|
||||
</option>
|
||||
</x-select-input>
|
||||
@error("role")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="password" value="New Password" />
|
||||
<x-text-input id="password" name="password" type="password" class="mt-1 w-full" />
|
||||
@error("password")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
<x-primary-button id="btn-save-info" form="update-user-info">Save</x-primary-button>
|
||||
</x-slot>
|
||||
</x-card>
|
@ -0,0 +1,69 @@
|
||||
<x-card>
|
||||
<x-slot name="title">
|
||||
{{ __("Update Password") }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="description">
|
||||
{{ __("Ensure your account is using a long, random password to stay secure.") }}
|
||||
</x-slot>
|
||||
|
||||
<form
|
||||
id="update-password"
|
||||
class="mt-6 space-y-6"
|
||||
hx-post="{{ route("profile.password") }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#update-password"
|
||||
hx-trigger="submit"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-save-password"
|
||||
>
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-input-label for="current_password" :value="__('Current Password')" />
|
||||
<x-text-input
|
||||
id="current_password"
|
||||
name="current_password"
|
||||
type="password"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
@error("current_password")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="password" :value="__('New Password')" />
|
||||
<x-text-input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
@error("password")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
||||
<x-text-input
|
||||
id="password_confirmation"
|
||||
name="password_confirmation"
|
||||
type="password"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
@error("password_confirmation")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
</form>
|
||||
<x-slot name="actions">
|
||||
<x-primary-button id="btn-save-password" form="update-password">
|
||||
{{ __("Save") }}
|
||||
</x-primary-button>
|
||||
</x-slot>
|
||||
</x-card>
|
9
resources/views/settings/users/show.blade.php
Normal file
9
resources/views/settings/users/show.blade.php
Normal file
@ -0,0 +1,9 @@
|
||||
<x-settings-layout>
|
||||
<x-slot name="pageTitle">Users</x-slot>
|
||||
|
||||
<x-container>
|
||||
@include("settings.users.partials.update-projects")
|
||||
|
||||
@include("settings.users.partials.update-user-info")
|
||||
</x-container>
|
||||
</x-settings-layout>
|
Reference in New Issue
Block a user