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

#50 - CRUD for schools frontend #104

Open
wants to merge 113 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 111 commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
599e3f3
create basic crud layout
AmonDeShir Oct 21, 2024
8c831c0
refactor: decouple from 'demo' and '87-login-page-improvements' branches
JokeUrSelf Oct 14, 2024
e9c5ba3
fix: duration input is cut when expanding the test
Oct 18, 2024
2df9491
fix: duration can be set to number the exceeds the max limit
JokeUrSelf Oct 18, 2024
02d21cd
fix: remove copying for multiple quizzes
JokeUrSelf Oct 18, 2024
44d976f
fix: delete button deletes all questions
JokeUrSelf Oct 18, 2024
ecce6b4
style: answer check button color and position
JokeUrSelf Oct 18, 2024
9cbc5fb
style: create new answers with empty content
JokeUrSelf Oct 18, 2024
27caa34
build: update packages
JokeUrSelf Oct 18, 2024
6289fa1
fix: linting
JokeUrSelf Oct 18, 2024
b590f15
chore: merge button logic
JokeUrSelf Oct 18, 2024
d2d0d0f
fix: remove time limits for datepicker
JokeUrSelf Oct 18, 2024
6729c1e
fix: error message is not fully visible
JokeUrSelf Oct 18, 2024
6b3009f
fix: `schedule_at` error message contains untranslated word 'now'
JokeUrSelf Oct 18, 2024
71d75d2
fix: wrong interpritation of time after quiz update
JokeUrSelf Oct 18, 2024
ef5c86a
style: add animation to quizzes occurence
JokeUrSelf Oct 18, 2024
ba61f3c
format: remove unused import
JokeUrSelf Oct 18, 2024
d0d09b7
style: add animations for icon-buttons
JokeUrSelf Oct 19, 2024
d8f7f79
apply suggestion: change 'Stworzony' na 'Utworzony'
JokeUrSelf Oct 19, 2024
768c34a
fix: sorting isn' t preserved after page reload
JokeUrSelf Oct 19, 2024
16e7702
fix: deletion alert doesn't close when clicking "Usuń"
JokeUrSelf Oct 19, 2024
3b6b0ac
chore: preserve state and scroll in request components by default
JokeUrSelf Oct 19, 2024
6db15d5
fix: seeder creates quizzes that can't exist
JokeUrSelf Oct 19, 2024
8d27ffb
feat: add placeholders for empty textareas
JokeUrSelf Oct 20, 2024
d1af3e9
fix: "Zaproś uczestników" redirects to entry that doesn't yet exist
JokeUrSelf Oct 21, 2024
949ad49
fix: typo in "Zaproś uczestników"
JokeUrSelf Oct 21, 2024
9f5255a
feat: add sorting by modification
JokeUrSelf Oct 21, 2024
c3a8e97
feat: make pre-publish validation message more specific
JokeUrSelf Oct 21, 2024
5b765ff
fix: publish button doesn't switch its state
JokeUrSelf Oct 21, 2024
b81932f
feat: add " - kopia" to the end of the tile of a copied quiz
JokeUrSelf Oct 21, 2024
b3e5014
format: `"` to `'`
JokeUrSelf Oct 21, 2024
6bc7c03
fix: sorting state is preserved between sessions
JokeUrSelf Oct 22, 2024
62d6cbe
style: change 'Nie można oddać testu.' to 'Nie można opublikować testu.'
JokeUrSelf Oct 22, 2024
1f19208
refactor: suggestions from code review
JokeUrSelf Oct 25, 2024
e376e2b
chore: add archieved quizzes to seeder
JokeUrSelf Oct 29, 2024
51e8afa
build: update packages
JokeUrSelf Nov 7, 2024
2445ab1
feat: add dropdown pointer positioning
JokeUrSelf Nov 7, 2024
e383a25
Merge branch 'main' of https://github.com/blumilksoftware/interns2024…
JokeUrSelf Nov 7, 2024
874df44
Merge branch 'main' into 26-admin-panel-for-managing-tests-frontend
JokeUrSelf Nov 8, 2024
6428de8
fix: quiz title underline
JokeUrSelf Nov 8, 2024
be7e0bb
Merge branch '26-admin-panel-for-managing-tests-frontend' of https://…
AmonDeShir Nov 8, 2024
46d26e0
fix: duration input doesn't resize fully
JokeUrSelf Nov 8, 2024
87005a7
feat: error handle empty question
JokeUrSelf Nov 9, 2024
e877fce
refactor: link button styling
JokeUrSelf Nov 9, 2024
e219f2f
create warning message box
AmonDeShir Nov 9, 2024
2063823
move sorting logic to a separate helper
AmonDeShir Nov 9, 2024
8d41573
fix: cloning doesn't add data to database
JokeUrSelf Nov 9, 2024
38b669b
fix: error highliting on question deletion reappears on the next ques…
JokeUrSelf Nov 9, 2024
b4740c9
feat: automatically remove empty answers
JokeUrSelf Nov 9, 2024
df8d86a
fix: revert update logic
JokeUrSelf Nov 9, 2024
85734d9
create basic crud for schools
AmonDeShir Nov 9, 2024
7f36b82
Merge branch '26-admin-panel-for-managing-tests-frontend' of https://…
AmonDeShir Nov 9, 2024
f90179f
simplify quizzes page
AmonDeShir Nov 9, 2024
c9eaa29
fix linter errors
AmonDeShir Nov 9, 2024
820d24e
fix code style
AmonDeShir Nov 9, 2024
ddfbc8d
rename value to address
AmonDeShir Nov 9, 2024
41cf1a7
add regon to school
AmonDeShir Nov 9, 2024
bc14211
Merge branch 'main' of https://github.com/blumilksoftware/interns2024…
AmonDeShir Nov 9, 2024
d5bf673
fix code style
AmonDeShir Nov 9, 2024
0f8bebc
add lintf
AmonDeShir Nov 10, 2024
95b2ba8
create mobile version
AmonDeShir Nov 10, 2024
ee7d7b3
add newItem slot
AmonDeShir Nov 10, 2024
7a006d5
remove limit from school controller
AmonDeShir Nov 10, 2024
a8d6014
create CrudInput
AmonDeShir Nov 10, 2024
00cb5a9
fix InputWrapper
AmonDeShir Nov 10, 2024
073b20a
remove force-full-screen-nav
AmonDeShir Nov 10, 2024
6c9d414
add prop pointer-position to Dropdown
AmonDeShir Nov 10, 2024
55d1251
change import button icon
AmonDeShir Nov 10, 2024
a152140
improve code style
AmonDeShir Nov 10, 2024
5c047d7
add comments explaining textarea height reset
AmonDeShir Nov 10, 2024
d5daa9d
Replace ButtonFrame with Button
AmonDeShir Nov 10, 2024
3a9f5af
allow manual implementation of the 'New Item' button
AmonDeShir Nov 10, 2024
ac1b740
improve ButtonFrame
AmonDeShir Nov 10, 2024
add105f
fix code style
AmonDeShir Nov 10, 2024
12a21d6
move resize none to vDynamicTextAreaHeight.ts
AmonDeShir Nov 11, 2024
c16daea
fix InputWrapper
AmonDeShir Nov 11, 2024
abc8723
add padding to ButtonFrame
AmonDeShir Nov 11, 2024
7331358
fix linter errors
AmonDeShir Nov 11, 2024
7fb771e
fix new item mode
AmonDeShir Nov 11, 2024
26eeb6c
Update resources/js/Pages/Admin/SchoolsPanel.vue
AmonDeShir Nov 12, 2024
4f03066
add regon validator
AmonDeShir Nov 20, 2024
b6b3e39
fix school seeder
AmonDeShir Nov 20, 2024
f48d1d0
fix code style
AmonDeShir Nov 20, 2024
a7b5e1c
Merge branch 'main' of https://github.com/blumilksoftware/interns2024…
AmonDeShir Nov 20, 2024
a490700
add zip-code input
AmonDeShir Nov 22, 2024
1669643
validate the length of the inputs
AmonDeShir Nov 22, 2024
7265086
create icon button
AmonDeShir Nov 23, 2024
5e195e1
create SortHelper
AmonDeShir Nov 24, 2024
e62ec08
add pagination to CRUDPage
AmonDeShir Nov 25, 2024
7d0876b
fix tests
AmonDeShir Nov 25, 2024
03a3ddb
fix abort mock
AmonDeShir Nov 25, 2024
fafdf7d
fix test
AmonDeShir Nov 25, 2024
ec3e31b
fix abort mock
AmonDeShir Nov 25, 2024
8102f70
fix test
AmonDeShir Nov 25, 2024
ae73b5c
add information about the number of fetched schools
AmonDeShir Nov 25, 2024
2432553
make filter case-insensitive
AmonDeShir Nov 26, 2024
5fd4acc
debounce searchbar
AmonDeShir Nov 26, 2024
9611a30
Merge branch 'main' of https://github.com/blumilksoftware/interns2024…
AmonDeShir Nov 26, 2024
3d41108
fix code style
AmonDeShir Nov 26, 2024
2cb0aed
fix sort test
AmonDeShir Nov 26, 2024
05e8ccd
add icon button
AmonDeShir Nov 26, 2024
6a6063e
rename QuizLayout to QuizPage
AmonDeShir Nov 26, 2024
20d4104
fix unsupported_field translation
AmonDeShir Nov 26, 2024
9f0fb79
fix sorter href
AmonDeShir Nov 26, 2024
cfedfb7
create no content component
AmonDeShir Nov 26, 2024
562b537
fix crud cancel button
AmonDeShir Nov 26, 2024
aabc799
rename ExapnsionToggleDynamicIcon to ExpansionToggleDynamicIcon
AmonDeShir Nov 26, 2024
388bb2c
hide delete button if quiz is locked
AmonDeShir Nov 26, 2024
f7b3bd1
change user name to firstname
AmonDeShir Nov 26, 2024
6fb2914
fix code style
AmonDeShir Nov 26, 2024
09ecd40
fix NoContent
AmonDeShir Nov 26, 2024
858c8b8
fix typo
AmonDeShir Nov 26, 2024
60d0a02
Update resources/js/Pages/Admin/Quizzes.vue
AmonDeShir Nov 26, 2024
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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ test-specific:
fix:
@docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'composer csf'

lintf:
@docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'npm run lintf'

analyse:
@docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'composer analyse'

Expand Down
15 changes: 15 additions & 0 deletions app/DTO/SchoolDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class SchoolDTO
{
public function __construct(
public string $name,
public string $regon,
public string $city,
public string $street,
public string $buildingNumber,
Expand All @@ -19,6 +20,7 @@ public static function createFromArray(array $data): self
{
$member = new self(
$data["nazwa"],
$data["regon"],
$data["miejscowosc"],
$data["ulica"],
$data["numerBudynku"],
Expand All @@ -28,4 +30,17 @@ public static function createFromArray(array $data): self

return $member;
}

public function toArray(): array
{
return [
"name" => $this->name,
"regon" => $this->regon,
"city" => $this->city,
"street" => $this->street,
"building_number" => $this->buildingNumber,
"apartment_number" => $this->apartmentNumber,
"zip_code" => $this->zipCode,
];
}
}
58 changes: 58 additions & 0 deletions app/Helpers/RegonHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace App\Helpers;

class RegonHelper
{
public const array WEIGHTS_SHORT = [8, 9, 2, 3, 4, 5, 6, 7];
public const array WEIGHTS_LONG = [2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8];

public static function generateShortRegon()
{
$digits = "";

for ($i = 0; $i < 8; $i++) {
$digits .= rand(0, 9);
}

$checksum = self::calculateChecksum(str_split($digits), self::WEIGHTS_SHORT);

return $digits . $checksum;
}

public static function generateLongRegon()
{
$digits = "";

for ($i = 0; $i < 13; $i++) {
$digits .= rand(0, 9);
}

$checksum = self::calculateChecksum(str_split($digits), self::WEIGHTS_LONG);

return $digits . $checksum;
}

/***
* @param array<string> $number
* @param array<int> $wages
*/
public static function calculateChecksum(array $number, array $wages): int
{
$sum = 0;

for ($i = 0; $i < count($wages); $i++) {
$sum += $wages[$i] * intval($number[$i]);
}

$checksum = $sum % 11;

if ($checksum === 10) {
return 0;
}

return $checksum;
}
}
70 changes: 70 additions & 0 deletions app/Helpers/SortHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace App\Helpers;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Lang;
use Symfony\Component\HttpFoundation\Response as Status;

class SortHelper
{
public function __construct(
private Request $request,
) {}

/**
* @param array<string> $allowedFields
* @param array<string> $ignoredFields
*/
public function sort(Builder $query, array $allowedFields, array $ignoredFields): Builder
{
[$field, $order] = $this->getSortParameters();

if (!in_array($field, $allowedFields, true)) {
if (in_array($field, $ignoredFields, true)) {
return $query;
}

abort(Status::HTTP_BAD_REQUEST, Lang::get("validation.custom.sorting.unsupported_field", ["attribute" => $field]));
}

return $query->orderBy($field, $order);
}

/**
* @return array<string>
*/
public function getSortParameters(): array
{
$field = $this->request->query("sort", "id");
$ascending = $this->request->query("order", "asc") === "asc";

return [$field, $ascending ? "asc" : "desc"];
}

public function search(Builder $query, string $field): Builder
{
$searchText = $this->request->query("search");

if ($searchText) {
return $query->where($field, "ilike", "%$searchText%");
}

return $query;
}

public function paginate(Builder $query): LengthAwarePaginator
{
$limit = (int)$this->request->query("limit", "50");

if (!$limit || $limit < 0) {
$limit = 50;
}

return $query->paginate($limit);
}
}
22 changes: 18 additions & 4 deletions app/Http/Controllers/QuizController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace App\Http\Controllers;

use App\Helpers\SortHelper;
use App\Http\Requests\QuizRequest;
use App\Http\Requests\UpdateQuizRequest;
use App\Http\Resources\QuizResource;
use App\Models\Quiz;
use App\Services\QuizUpdateService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
Expand All @@ -19,11 +21,12 @@

class QuizController extends Controller
{
public function index(): Response
public function index(Request $request, SortHelper $sorter): Response
{
$quizzes = Quiz::query()
->with("questions.answers")
->get();
$query = $sorter->sort(Quiz::query()->with("questions.answers"), ["id", "title", "updated_at", "created_at"], []);
$query = $this->filterArchivedQuizzes($query, $request);
$query = $sorter->search($query, "title");
$quizzes = $sorter->paginate($query);

return Inertia::render("Admin/Quizzes", ["quizzes" => QuizResource::collection($quizzes)]);
}
Expand Down Expand Up @@ -104,4 +107,15 @@ public function assign(Request $request, Quiz $quiz): RedirectResponse
->back()
->with("status", "Przypisano do testu");
}

private function filterArchivedQuizzes(Builder $query, Request $request): Builder
{
$showArchived = $request->query("archived", "false") === "true";

if (!$showArchived) {
return $query->whereNull("locked_at")->orWhereDate("scheduled_at", ">", Carbon::now());
}

return $query;
}
}
42 changes: 39 additions & 3 deletions app/Http/Controllers/SchoolsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
namespace App\Http\Controllers;

use App\Enums\Voivodeship;
use App\Helpers\SortHelper;
use App\Http\Requests\SchoolRequest;
use App\Http\Resources\SchoolResource;
use App\Jobs\FetchSchoolsJob;
use App\Models\School;
use Illuminate\Bus\Batch;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Bus;
Expand All @@ -24,17 +26,24 @@

class SchoolsController extends Controller
{
public function index(): Response
public function index(SortHelper $sorter): Response
{
return Inertia::render("Admin/SchoolsPanel", ["schools" => SchoolResource::collection(School::all())]);
$query = $sorter->sort(School::query(), ["id", "name", "regon", "updated_at", "created_at"], ["students", "address"]);
$query = $this->sortByStudents($query, $sorter);
$query = $this->sortByAddress($query, $sorter);
$query = $sorter->search($query, "name");
$schools = $sorter->paginate($query);

return Inertia::render("Admin/SchoolsPanel", ["schools" => SchoolResource::collection($schools)]);
}

public function store(SchoolRequest $request): RedirectResponse
{
School::query()->create($request->validated());

return redirect()
->back();
->back()
->with("status", "Szkoła została dodana.");
}

public function update(SchoolRequest $request, School $school): RedirectResponse
Expand Down Expand Up @@ -67,6 +76,7 @@ public function fetch(): JsonResponse
$jobs = $voivodeships->map(fn(Voivodeship $voivodeships): FetchSchoolsJob => new FetchSchoolsJob($voivodeships, $schoolTypes));
$batch = Bus::batch($jobs)->finally(fn(): bool => Cache::delete("fetch_schools"))->dispatch();
Cache::set("fetch_schools", $batch->id);
Cache::forget("fetched_schools");

return response()->json(["message" => "Pobieranie rozpoczęte"], Status::HTTP_OK);
}
Expand All @@ -75,6 +85,7 @@ public function status(): JsonResponse
{
return response()->json([
"done" => !$this->isFetching(),
"count" => (int)Cache::get("fetched_schools"),
]);
}

Expand All @@ -95,4 +106,29 @@ protected function findBatch(): ?Batch

return Bus::findBatch($batchId);
}

private function sortByStudents(Builder $query, SortHelper $sorter): Builder
{
[$field, $order] = $sorter->getSortParameters();

if ($field === "students") {
return $query->withCount("users")->orderBy("users_count", $order);
}

return $query;
}

private function sortByAddress(Builder $query, SortHelper $sorter): Builder
{
[$field, $order] = $sorter->getSortParameters();

if ($field === "address") {
return $query->orderBy("city", $order)
->orderBy("zip_code", $order)
->orderBy("street", $order)
->orderBy("name", $order);
}

return $query;
}
}
2 changes: 1 addition & 1 deletion app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function anonymize(User $user): RedirectResponse
{
$this->authorize("anonymize", $user);
$user->update([
"name" => "Anonymous",
"firstname" => "Anonymous",
"surname" => "User",
"email" => "anonymous" . $user->id . "@email",
"is_anonymized" => true,
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/Auth/RegisterUserRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function rules(): array
{
return [
"email" => ["required", "string", "email:rfc,dns", "max:255"],
"name" => ["required", "string", "max:255"],
"firstname" => ["required", "string", "max:255"],
"surname" => ["required", "string", "max:255"],
"password" => ["required", "string", "min:8"],
"school_id" => ["required", "integer", "exists:schools,id"],
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/QuizRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function prepareForValidation(): void
public function rules(): array
{
return [
"title" => ["required", "string"],
"title" => ["required", "string", "max:255"],
"scheduled_at" => ["date", "after:now"],
"duration" => ["numeric", "min:1", "max:2147483647"],
];
Expand Down
29 changes: 23 additions & 6 deletions app/Http/Requests/SchoolRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Http\Requests;

use App\Rules\Regon;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;

Expand All @@ -14,18 +15,34 @@ public function authorize(): bool
return true;
}

public function prepareForValidation(): void
{
if ($this->has("buildingNumber")) {
$this->merge(["building_number" => $this->input("buildingNumber")]);
}

if ($this->has("apartmentNumber")) {
$this->merge(["apartment_number" => $this->input("apartmentNumber")]);
}

if ($this->has("zipCode")) {
$this->merge(["zip_code" => $this->input("zipCode")]);
}
}

/**
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
"name" => ["required", "string"],
"city" => ["required", "string"],
"street" => ["required", "string"],
"building_number" => ["required", "string"],
"apartment_number" => ["string"],
"zip_code" => ["required", "string"],
"name" => ["required", "string", "max:255"],
"city" => ["required", "string", "max:255"],
"regon" => ["required", "string", new Regon()],
"street" => ["required", "string", "max:255"],
"building_number" => ["required", "string", "max:255"],
"apartment_number" => ["string", "nullable", "max:255"],
"zip_code" => ["required", "string", "regex:/^\d{2}-\d{3}$/"],
];
}
}
2 changes: 1 addition & 1 deletion app/Http/Requests/UpdateQuizRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function prepareForValidation(): void
public function rules(): array
{
return [
"title" => ["required", "string"],
"title" => ["required", "string", "max:255"],
"scheduled_at" => ["date", "after:now"],
"duration" => ["integer", "min:1", "max:2147483647"],
"questions" => ["array"],
Expand Down
Loading
Loading