Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve admin tools #71

Merged
merged 9 commits into from
Sep 17, 2023
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions app/Http/Controllers/CRUDController.php
Original file line number Diff line number Diff line change
@@ -25,23 +25,38 @@ abstract class CRUDController extends Controller
/**
* The validation rules.
*
* @var array<int, string>
* @var array<string, string | array<int|string>>
*/
protected array $rules = [];

/**
* The columns to search in.
*
* @var array<int, string>
*/
protected array $search = [];

/**
* The validation rules for the store method.
*
* @var array<int, string>|null
* @param T $old The old model.
* @return array<string, string | array<int|string>>
*/
protected ?array $storeRules;
protected function storeRules(): array
{
return $this->rules;
}

/**
* The validation rules for the update method.
*
* @var array<int, string>|null
* @param T $old The old model.
* @return array<string, string | array<int|string>>
*/
protected ?array $updateRules;
protected function updateRules($old): array
{
return $this->rules;
}

/**
* The array to include with the views.
@@ -53,9 +68,26 @@ protected function with(): array
return [];
}

public function index()
public function index(Request $request)
{
$items = $this->model::paginate(10);
$sort_by = $request->query('sort_by', 'id');
$sort_dir = $request->query('sort_dir', 'asc');
$query = $this->model::orderBy($sort_by, $sort_dir);

$search = $request->query('query');

if ($search) {
$search = explode(' ', $search);
foreach ($search as $searchTerm) {
$query->where(function ($query) use ($searchTerm) {
foreach ($this->search as $column) {
$query->orWhere($column, 'ILIKE', "%{$searchTerm}%");
}
});
}
}

$items = $query->paginate()->withQueryString();

$with = $this->with();

@@ -113,7 +145,7 @@ protected function created(array $new): ?array

public function store(Request $request)
{
$validated = $request->validate($this->storeRules ?? $this->rules);
$validated = $request->validate($this->storeRules());

$newValues = $this->created($validated);

@@ -129,22 +161,22 @@ public function store(Request $request)
* The returned array will be used to update the model, unless
* null is returned, in which case the model will not be updated.
*
* @param array<string, mixed> $old The old values of the model.
* @param T $old The old model.
* @param array<string, mixed> $new The validated values.
* @return array<string, mixed>|null
*/
protected function updated(array $old, array $new): ?array
protected function updated($old, array $new): ?array
{
return $new;
}

public function update(Request $request, $id)
{
$validated = $request->validate($this->updateRules ?? $this->rules);

$model = $this->model::find($id);

$newValues = $this->updated($model->toArray(), $validated);
$validated = $request->validate($this->updateRules($model));

$newValues = $this->updated($model, $validated);

if ($newValues !== null) {
$model->update($newValues);
@@ -158,9 +190,9 @@ public function update(Request $request, $id)
* If true is returned, the model will be deleted.
* If false is returned, the model will not be deleted.
*
* @param array<string, mixed> $old The old values of the model.
* @param T $old The old model.
*/
protected function destroyed(array $old): bool
protected function destroyed($old): bool
{
return true;
}
2 changes: 2 additions & 0 deletions app/Http/Controllers/EditionCRUDController.php
Original file line number Diff line number Diff line change
@@ -14,4 +14,6 @@ class EditionCRUDController extends CRUDController
'name' => 'required|string',
'year' => 'required|integer',
];

protected array $search = ['name', 'year'];
}
2 changes: 2 additions & 0 deletions app/Http/Controllers/EventCRUDController.php
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ class EventCRUDController extends CRUDController
'edition_id' => 'required|exists:editions,id',
];

protected array $search = ['name', 'topic', 'capacity'];

protected function with(): array
{
return [
2 changes: 2 additions & 0 deletions app/Http/Controllers/ProductCRUDController.php
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@ class ProductCRUDController extends CRUDController
'edition_id' => 'required|exists:editions,id',
];

protected array $search = ['name', 'price', 'stock'];

protected function with(): array
{
return [
4 changes: 3 additions & 1 deletion app/Http/Controllers/QuestCRUDController.php
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ protected function with(): array
];
}

protected array $search = ['name', 'points', 'category'];

protected function created(array $new): ?array
{
$requirement = explode(';', $new['requirement']);
@@ -48,7 +50,7 @@ protected function created(array $new): ?array
];
}

protected function updated(array $old, array $new): ?array
protected function updated($old, array $new): ?array
{
$requirement = explode(';', $new['requirement']);

6 changes: 4 additions & 2 deletions app/Http/Controllers/SpeakerCRUDController.php
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ class SpeakerCRUDController extends CRUDController
'event_id' => 'required|exists:events,id',
];

protected array $search = ['name', 'title', 'description', 'organization'];

protected function with(): array
{
return [
@@ -56,10 +58,10 @@ protected function created(array $new): ?array
return null;
}

protected function updated(array $old, array $new): ?array
protected function updated($old, array $new): ?array
{
if (isset($new['social_media'])) {
$socialMedia = SocialMedia::find($old['social_media_id']);
$socialMedia = $old->socialMedia;
if ($socialMedia === null) {
$socialMedia = SocialMedia::create($new['social_media']);
} else {
4 changes: 3 additions & 1 deletion app/Http/Controllers/SponsorCRUDController.php
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@ class SponsorCRUDController extends CRUDController
'company_id' => 'required|exists:companies,id',
];

protected array $search = ['tier'];

protected function created(array $new): ?array
{
return [
@@ -27,7 +29,7 @@ protected function created(array $new): ?array
];
}

protected function updated(array $old, array $new): ?array
protected function updated($old, array $new): ?array
{
return [
'edition_id' => $new['edition_id'],
15 changes: 12 additions & 3 deletions app/Http/Controllers/UserCRUDController.php
Original file line number Diff line number Diff line change
@@ -28,6 +28,15 @@ class UserCRUDController extends CRUDController
'social_media.website' => 'sometimes|nullable|string|url',
];

protected array $search = ['name', 'email'];

protected function updateRules($old): array
{
return array_merge($this->rules, [
'email' => 'required|string|email|max:255|unique:users,email,'.$old->id,
]);
}

protected function created(array $new): ?array
{
$type = match ($new['type']) {
@@ -59,21 +68,21 @@ protected function created(array $new): ?array
return null;
}

protected function updated(array $old, array $new): ?array
protected function updated($old, array $new): ?array
{
$type = match ($new['type']) {
'student' => Student::class,
'company' => Company::class,
'admin' => Admin::class,
};

if ($old['usertype_type'] !== $type) {
if ($old->usertype_type !== $type) {
// This should never happen
throw new \Exception('Cannot change user type');
}

if ($new['type'] !== 'admin' && isset($new['social_media'])) {
$socialMedia = SocialMedia::find($old['social_media_id']);
$socialMedia = $old->social_media;
ttoino marked this conversation as resolved.
Show resolved Hide resolved
if ($socialMedia === null) {
$socialMedia = SocialMedia::create($new['social_media']);
} else {
41 changes: 39 additions & 2 deletions resources/js/Components/CRUD/Header.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
<script setup lang="ts">
import { Link } from "@inertiajs/vue3";
import { computed } from "vue";
import route from "ziggy-js";

interface Props {
sortBy?: string;
}

defineProps<Props>();
const props = defineProps<Props>();

const sort = computed(() => {
if (!props.sortBy) return { url: "#", icon: "io-swap-vertical" };

const searchParams = new URLSearchParams(window.location.search);

const currentColumn = searchParams.get("sort_by");
const currentDirection = searchParams.get("sort_dir");

searchParams.set("sort_by", props.sortBy);
searchParams.set(
"sort_dir",
currentColumn === props.sortBy && currentDirection === "asc"
? "desc"
: "asc",
);

const router = route();
return {
url: route(router.current() ?? "", {
...router.params,
...Object.fromEntries(searchParams.entries()),
}),
icon:
currentColumn === props.sortBy
? currentDirection === "asc"
? "io-arrow-down"
: "io-arrow-up"
: "io-swap-vertical",
};
});
</script>

<template>
<th class="px-4 py-2 text-start last:text-right">
<slot></slot>
<v-icon v-if="sortBy" name="io-swap-vertical" class="ml-2" />
<Link v-if="sortBy" :href="sort.url" preserve-scroll>
<v-icon :name="sort.icon" class="ml-2" />
</Link>
</th>
</template>
8 changes: 6 additions & 2 deletions resources/js/Components/Navbar.vue
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ const options = {
/>
</div>
<div class="ml-4 hidden w-full min-w-fit md:flex lg:gap-4">
<template v-for="page in Object.keys(page_routes)">
<template v-for="page in Object.keys(page_routes)" :key="page">
<NavLink
:href="route('dashboard')"
:active="page == 'Sponsors'"
@@ -61,6 +61,7 @@ const options = {
<template #content>
<template
v-for="activity in Object.keys(activity_routes)"
:key="activity"
>
<DropdownLink :href="route('dashboard')">
{{ activity }}
@@ -74,7 +75,10 @@ const options = {
<DropdownTrigger> 2023 </DropdownTrigger>
</template>
<template #content>
<template v-for="edition in edition_routes">
<template
v-for="edition in edition_routes"
:key="edition"
>
<DropdownLink :href="route('dashboard')">
{{ edition }}
</DropdownLink>
22 changes: 20 additions & 2 deletions resources/js/Layouts/CRUDLayout.vue
Original file line number Diff line number Diff line change
@@ -4,14 +4,30 @@ import PaginationLinks from "@/Components/PaginationLinks.vue";
import type Model from "@/Types/Model";
import type Paginated from "@/Types/Paginated";
import AppLayout from "./AppLayout.vue";
import { Link } from "@inertiajs/vue3";
import { Link, router } from "@inertiajs/vue3";
import route from "ziggy-js";
import TextInput from "@/Components/TextInput.vue";
import { ref, watch } from "vue";

defineProps<{
items: Paginated<T>;
title: string;
name: string;
}>();

const query = ref(new URLSearchParams(location.search).get("query") ?? "");

watch(query, (query) => {
const ziggy = route();

router.replace(
route(ziggy.current() ?? "#", { ...ziggy.params, query, page: 1 }),
{
preserveState: true,
preserveScroll: true,
},
);
});
</script>

<template>
@@ -20,7 +36,9 @@ defineProps<{
<header
class="flex flex-row flex-wrap items-center justify-between gap-4"
>
<h2 class="text-2xl"><slot name="heading"></slot></h2>
<h2 class="mr-auto text-2xl"><slot name="heading"></slot></h2>

<TextInput v-model="query" label="Pesquisar" type="search" />

<Link :href="route(`admin.${name}.create`)">Novo</Link>
</header>
2 changes: 1 addition & 1 deletion resources/js/Pages/CRUD/Event/Index.vue
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ const editions = computed<Record<number, string>>(() =>
<Header sort-by="name">Nome</Header>
<Header sort-by="date_start">Data de Início</Header>
<Header sort-by="date_end">Data de Fim</Header>
<Header sort-by="edition_id">Edição</Header>
<Header>Edição</Header>
</HeaderRow>
</template>

4 changes: 2 additions & 2 deletions resources/js/Pages/CRUD/Product/Index.vue
Original file line number Diff line number Diff line change
@@ -33,8 +33,8 @@ const editions = computed<Record<number, string>>(() =>
<HeaderRow>
<Header sort-by="name">Nome</Header>
<Header sort-by="price">Preço</Header>
<Header sort-by="points">Stock</Header>
<Header sort-by="edition_id">Edição</Header>
<Header sort-by="stock">Stock</Header>
<Header>Edição</Header>
</HeaderRow>
</template>

Loading