diff --git a/app/Actions/CloseQuizSubmissionAction.php b/app/Actions/CloseQuizSubmissionAction.php deleted file mode 100644 index 91cea39e..00000000 --- a/app/Actions/CloseQuizSubmissionAction.php +++ /dev/null @@ -1,17 +0,0 @@ -closed_at = Carbon::now(); - $submission->save(); - } -} diff --git a/app/Actions/CloseUserQuizAction.php b/app/Actions/CloseUserQuizAction.php new file mode 100644 index 00000000..5db6ff98 --- /dev/null +++ b/app/Actions/CloseUserQuizAction.php @@ -0,0 +1,17 @@ +closed_at = Carbon::now(); + $userQuiz->save(); + } +} diff --git a/app/Helpers/RegonHelper.php b/app/Helpers/RegonHelper.php new file mode 100644 index 00000000..9235e888 --- /dev/null +++ b/app/Helpers/RegonHelper.php @@ -0,0 +1,58 @@ + $number + * @param array $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; + } +} diff --git a/app/Helpers/SortHelper.php b/app/Helpers/SortHelper.php new file mode 100644 index 00000000..f12b2798 --- /dev/null +++ b/app/Helpers/SortHelper.php @@ -0,0 +1,70 @@ + $allowedFields + * @param array $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 + */ + public function getSortParameters(): array + { + $field = $this->request->query("sort", "id"); + $ascending = $this->request->query("order", "desc") === "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); + } +} diff --git a/app/Http/Controllers/AuthenticateSessionController.php b/app/Http/Controllers/AuthenticateSessionController.php index 58fb4374..61cc4a9e 100644 --- a/app/Http/Controllers/AuthenticateSessionController.php +++ b/app/Http/Controllers/AuthenticateSessionController.php @@ -21,9 +21,7 @@ public function authenticate(AuthenticateSessionRequest $request): RedirectRespo if (auth()->attempt($credentials)) { $request->session()->regenerate(); - return $request->user()->hasRole(["admin", "super_admin"]) - ? Redirect::route("admin.quizzes.index") - : Redirect::route("dashboard"); + return redirect()->route("home"); } throw ValidationException::withMessages([ diff --git a/app/Http/Controllers/ContestController.php b/app/Http/Controllers/ContestController.php index 34e0fcf3..5d595d44 100644 --- a/app/Http/Controllers/ContestController.php +++ b/app/Http/Controllers/ContestController.php @@ -5,10 +5,11 @@ namespace App\Http\Controllers; use App\Http\Resources\QuizResource; -use App\Http\Resources\QuizSubmissionResource; use App\Http\Resources\SchoolResource; +use App\Http\Resources\UserQuizResource; use App\Models\Quiz; use App\Models\School; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; @@ -22,11 +23,16 @@ public function index(): Response return Inertia::render("Home", ["schools" => SchoolResource::collection($schools)]); } - public function create(Request $request): Response + public function create(Request $request): RedirectResponse|Response { $user = $request->user(); - $submissions = $user->quizSubmissions() - ->with(["answerRecords.question.answers", "quiz"]) + + if ($user->hasRole(["admin", "super_admin"])) { + return redirect()->route("admin.quizzes.index"); + } + + $userQuizzes = $user->userQuizzes() + ->with(["userQuestions.question.answers", "quiz"]) ->get(); $quizzes = Quiz::query() @@ -35,7 +41,7 @@ public function create(Request $request): Response ->get(); return Inertia::render("User/Dashboard", [ - "submissions" => QuizSubmissionResource::collection($submissions), + "userQuizzes" => UserQuizResource::collection($userQuizzes), "quizzes" => QuizResource::collection($quizzes), ]); } diff --git a/app/Http/Controllers/QuizController.php b/app/Http/Controllers/QuizController.php index fae15ed7..5f46f53c 100644 --- a/app/Http/Controllers/QuizController.php +++ b/app/Http/Controllers/QuizController.php @@ -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; @@ -19,15 +21,24 @@ 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)]); } + public function show(Quiz $quiz): Response + { + return Inertia::render( + "Admin/QuizDemo", + ["quiz" => $quiz->load("questions.answers")], + ); + } + public function store(QuizRequest $request): RedirectResponse { Quiz::query()->create($request->validated()); @@ -78,12 +89,12 @@ public function unlock(Quiz $quiz): RedirectResponse ->with("status", "Publikacja testu została wycofana"); } - public function createSubmission(Request $request, Quiz $quiz): RedirectResponse + public function createUserQuiz(Request $request, Quiz $quiz): RedirectResponse { $user = $request->user(); - $submission = $quiz->createSubmission($user); + $userQuiz = $quiz->createUserQuiz($user); - return redirect("/submissions/{$submission->id}/"); + return redirect("/quizzes/{$userQuiz->id}/"); } public function assign(Request $request, Quiz $quiz): RedirectResponse @@ -96,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->orWhere(fn(Builder $query) => $query->whereNull("locked_at")->orWhereDate("scheduled_at", ">", Carbon::now())); + } + + return $query; + } } diff --git a/app/Http/Controllers/QuizSubmissionController.php b/app/Http/Controllers/QuizSubmissionController.php deleted file mode 100644 index 9a11b434..00000000 --- a/app/Http/Controllers/QuizSubmissionController.php +++ /dev/null @@ -1,39 +0,0 @@ -load(["answerRecords.question.answers"]); - - return Inertia::render("User/Quiz", ["submission" => QuizSubmissionResource::make($quizSubmission)]); - } - - public function close(QuizSubmission $quizSubmission, CloseQuizSubmissionAction $action): RedirectResponse - { - $action->execute($quizSubmission); - - return redirect()->route("submissions.result", $quizSubmission->id)->with("status", "Test został oddany."); - } - - public function result(QuizSubmission $quizSubmission): Response - { - $quizSubmission->load(["answerRecords.question.answers", "quiz"]); - - return Inertia::render("User/QuizResult", [ - "submission" => QuizSubmissionResource::make($quizSubmission), - "hasRanking" => $quizSubmission->quiz->isRankingPublished, - ]); - } -} diff --git a/app/Http/Controllers/RankingController.php b/app/Http/Controllers/RankingController.php index 528a58d8..8a652588 100644 --- a/app/Http/Controllers/RankingController.php +++ b/app/Http/Controllers/RankingController.php @@ -9,7 +9,7 @@ use App\Http\Resources\QuizResource; use App\Http\Resources\RankingResource; use App\Models\Quiz; -use App\Models\QuizSubmission; +use App\Models\UserQuiz; use Illuminate\Http\RedirectResponse; use Inertia\Inertia; use Inertia\Response; @@ -18,14 +18,14 @@ class RankingController extends Controller { public function index(Quiz $quiz): Response { - $submissions = QuizSubmission::query() + $userQuizzes = UserQuiz::query() ->where("quiz_id", $quiz->id) ->with("user.school") ->get(); return Inertia::render("Admin/Ranking", [ "quiz" => QuizResource::make($quiz), - "rankings" => RankingResource::collection($submissions), + "rankings" => RankingResource::collection($userQuizzes), ]); } @@ -33,14 +33,14 @@ public function indexUser(Quiz $quiz): Response { $this->authorize("viewUserRanking", $quiz); - $submissions = QuizSubmission::query() + $userQuizzes = UserQuiz::query() ->where("quiz_id", $quiz->id) ->with("user.school") ->get(); return Inertia::render("User/Ranking", [ "quiz" => QuizResource::make($quiz), - "rankings" => RankingResource::collection($submissions), + "rankings" => RankingResource::collection($userQuizzes), ]); } diff --git a/app/Http/Controllers/SchoolsController.php b/app/Http/Controllers/SchoolsController.php index 8ddf6f14..999bd1cb 100644 --- a/app/Http/Controllers/SchoolsController.php +++ b/app/Http/Controllers/SchoolsController.php @@ -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; @@ -24,9 +26,15 @@ 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 @@ -68,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); } @@ -76,6 +85,7 @@ public function status(): JsonResponse { return response()->json([ "done" => !$this->isFetching(), + "count" => (int)Cache::get("fetched_schools"), ]); } @@ -96,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; + } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 704839bf..26149992 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -52,7 +52,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, diff --git a/app/Http/Controllers/AnswerRecordController.php b/app/Http/Controllers/UserQuestionController.php similarity index 53% rename from app/Http/Controllers/AnswerRecordController.php rename to app/Http/Controllers/UserQuestionController.php index 580895ab..f566be4d 100644 --- a/app/Http/Controllers/AnswerRecordController.php +++ b/app/Http/Controllers/UserQuestionController.php @@ -5,16 +5,16 @@ namespace App\Http\Controllers; use App\Models\Answer; -use App\Models\AnswerRecord; +use App\Models\UserQuestion; use Illuminate\Http\RedirectResponse; use function redirect; -class AnswerRecordController extends Controller +class UserQuestionController extends Controller { - public function answer(AnswerRecord $answerRecord, Answer $answer): RedirectResponse + public function answer(UserQuestion $userQuestion, Answer $answer): RedirectResponse { - $answerRecord->answer()->associate($answer)->save(); + $userQuestion->answer()->associate($answer)->save(); return redirect()->back(); } diff --git a/app/Http/Controllers/UserQuizController.php b/app/Http/Controllers/UserQuizController.php new file mode 100644 index 00000000..1cab5eb0 --- /dev/null +++ b/app/Http/Controllers/UserQuizController.php @@ -0,0 +1,39 @@ +load(["userQuestions.question.answers"]); + + return Inertia::render("User/Quiz", ["userQuiz" => UserQuizResource::make($userQuiz)]); + } + + public function close(UserQuiz $userQuiz, CloseUserQuizAction $action): RedirectResponse + { + $action->execute($userQuiz); + + return redirect()->route("userQuizzes.result", $userQuiz->id)->with("status", "Test został oddany."); + } + + public function result(UserQuiz $userQuiz): Response + { + $userQuiz->load(["userQuestions.question.answers", "quiz"]); + + return Inertia::render("User/QuizResult", [ + "userQuiz" => UserQuizResource::make($userQuiz), + "hasRanking" => $userQuiz->quiz->isRankingPublished, + ]); + } +} diff --git a/app/Http/Integrations/RSPOConnector/RSPOConnector.php b/app/Http/Integrations/RSPOConnector/RSPOConnector.php index 21b156f5..eb310bb7 100644 --- a/app/Http/Integrations/RSPOConnector/RSPOConnector.php +++ b/app/Http/Integrations/RSPOConnector/RSPOConnector.php @@ -16,7 +16,7 @@ class RSPOConnector extends Connector implements HasPagination public function resolveBaseUrl(): string { - return "https://api-rspo.mein.gov.pl/api"; + return "https://api-rspo.men.gov.pl/api"; } public function paginate(Request $request): Paginator diff --git a/app/Http/Middleware/EnsureQuizIsNotAlreadyStarted.php b/app/Http/Middleware/EnsureQuizIsNotAlreadyStarted.php index 19924b42..61aec8d7 100644 --- a/app/Http/Middleware/EnsureQuizIsNotAlreadyStarted.php +++ b/app/Http/Middleware/EnsureQuizIsNotAlreadyStarted.php @@ -4,7 +4,7 @@ namespace App\Http\Middleware; -use App\Models\QuizSubmission; +use App\Models\UserQuiz; use Closure; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -17,10 +17,10 @@ public function handle(Request $request, Closure $next): Response|RedirectRespon $user = $request->user(); $quiz = $request->route()->parameter("quiz"); - $submission = QuizSubmission::query()->where(["quiz_id" => $quiz->id, "user_id" => $user->id])->first(); + $userQuiz = UserQuiz::query()->where(["quiz_id" => $quiz->id, "user_id" => $user->id])->first(); - if ($submission) { - return redirect(route("submissions.show", $submission->id)); + if ($userQuiz) { + return redirect(route("userQuizzes.show", $userQuiz->id)); } return $next($request); diff --git a/app/Http/Requests/Auth/RegisterUserRequest.php b/app/Http/Requests/Auth/RegisterUserRequest.php index 4495fc06..d9aacfb8 100644 --- a/app/Http/Requests/Auth/RegisterUserRequest.php +++ b/app/Http/Requests/Auth/RegisterUserRequest.php @@ -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"], diff --git a/app/Http/Requests/QuizRequest.php b/app/Http/Requests/QuizRequest.php index 35918158..716abe07 100644 --- a/app/Http/Requests/QuizRequest.php +++ b/app/Http/Requests/QuizRequest.php @@ -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"], ]; diff --git a/app/Http/Requests/SchoolRequest.php b/app/Http/Requests/SchoolRequest.php index f3cb16d8..fff205fa 100644 --- a/app/Http/Requests/SchoolRequest.php +++ b/app/Http/Requests/SchoolRequest.php @@ -4,6 +4,7 @@ namespace App\Http\Requests; +use App\Rules\Regon; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Foundation\Http\FormRequest; @@ -35,13 +36,13 @@ public function prepareForValidation(): void public function rules(): array { return [ - "name" => ["required", "string"], - "city" => ["required", "string"], - "regon" => ["required", "string"], - "street" => ["required", "string"], - "building_number" => ["required", "string"], - "apartment_number" => ["string", "nullable"], - "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}$/"], ]; } } diff --git a/app/Http/Requests/UpdateQuizRequest.php b/app/Http/Requests/UpdateQuizRequest.php index 2f6e89f2..ac40815a 100644 --- a/app/Http/Requests/UpdateQuizRequest.php +++ b/app/Http/Requests/UpdateQuizRequest.php @@ -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"], diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php index b97c3d16..1ee2b303 100644 --- a/app/Http/Requests/UserRequest.php +++ b/app/Http/Requests/UserRequest.php @@ -17,7 +17,7 @@ public function rules(): array { return [ "email" => ["required", "string", "email", "max:255", "unique:users,email," . $this->user->id], - "name" => ["required", "string", "max:255"], + "firstname" => ["required", "string", "max:255"], "surname" => ["required", "string", "max:255"], "school_id" => ["required", "integer", "exists:schools,id"], ]; diff --git a/app/Http/Resources/RankingResource.php b/app/Http/Resources/RankingResource.php index 6d6dd84e..3d1fcd8d 100644 --- a/app/Http/Resources/RankingResource.php +++ b/app/Http/Resources/RankingResource.php @@ -19,7 +19,7 @@ public function toArray(Request $request): array return [ "user" => [ "id" => $this->user->id, - "name" => $isAdmin ? $this->user->name : null, + "firstname" => $isAdmin ? $this->user->firstname : null, "surname" => $isAdmin ? $this->user->surname : null, "school" => $this->user->school, ], diff --git a/app/Http/Resources/AnswerRecordResource.php b/app/Http/Resources/UserQuestionResource.php similarity index 90% rename from app/Http/Resources/AnswerRecordResource.php rename to app/Http/Resources/UserQuestionResource.php index ac666847..9a533401 100644 --- a/app/Http/Resources/AnswerRecordResource.php +++ b/app/Http/Resources/UserQuestionResource.php @@ -10,17 +10,17 @@ use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Collection; -class AnswerRecordResource extends JsonResource +class UserQuestionResource extends JsonResource { public function toArray(Request $request): array { return [ "id" => $this->id, - "question" => $this->question->text, + "text" => $this->question->text, "createdAt" => $this->created_at, "updatedAt" => $this->updated_at, "closed" => $this->isClosed, - "selected" => $this->answer_id, + "selectedAnswer" => $this->answer_id, "answers" => $this->questionAnswersToArray($this->question)->shuffle(), ]; } diff --git a/app/Http/Resources/QuizSubmissionResource.php b/app/Http/Resources/UserQuizResource.php similarity index 67% rename from app/Http/Resources/QuizSubmissionResource.php rename to app/Http/Resources/UserQuizResource.php index fc725b1e..36149b17 100644 --- a/app/Http/Resources/QuizSubmissionResource.php +++ b/app/Http/Resources/UserQuizResource.php @@ -7,20 +7,19 @@ use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -class QuizSubmissionResource extends JsonResource +class UserQuizResource extends JsonResource { public function toArray(Request $request): array { return [ "id" => $this->id, - "name" => $this->quiz->title, + "title" => $this->quiz->title, "createdAt" => $this->created_at, "updatedAt" => $this->updated_at, - "openedAt" => $this->quiz->scheduled_at, "closedAt" => $this->closed_at, "closed" => $this->isClosed, "quiz" => $this->quiz_id, - "answers" => AnswerRecordResource::collection($this->answerRecords->shuffle()), + "questions" => UserQuestionResource::collection($this->userQuestions->shuffle()), ]; } } diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 86f48451..ae230c78 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -16,7 +16,7 @@ public function toArray(Request $request): array { return [ "id" => $this->id, - "name" => $this->name, + "firstname" => $this->firstname, "surname" => $this->surname, "email" => $this->email, "school" => SchoolResource::make($this->school), diff --git a/app/Jobs/FetchSchoolsJob.php b/app/Jobs/FetchSchoolsJob.php index 1ddef93f..4c37e676 100644 --- a/app/Jobs/FetchSchoolsJob.php +++ b/app/Jobs/FetchSchoolsJob.php @@ -10,6 +10,7 @@ use Illuminate\Bus\Batchable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Queue\Queueable; +use Illuminate\Support\Facades\Cache; class FetchSchoolsJob implements ShouldQueue { @@ -26,6 +27,7 @@ public function __construct( public function handle(GetSchoolDataService $service): void { - $service->getSchools($this->voivodeship, $this->schoolTypes); + $added_schools = $service->getSchools($this->voivodeship, $this->schoolTypes); + Cache::set("fetched_schools", $added_schools); } } diff --git a/app/Models/Quiz.php b/app/Models/Quiz.php index 0a405cf2..87d4bdd2 100644 --- a/app/Models/Quiz.php +++ b/app/Models/Quiz.php @@ -18,8 +18,8 @@ * @property string $title * @property Carbon $created_at * @property Carbon $updated_at - * @property Carbon $scheduled_at - * @property Carbon $ranking_published_at + * @property ?Carbon $scheduled_at + * @property ?Carbon $ranking_published_at * @property ?Carbon $locked_at * @property ?int $duration * @property bool $isLocked @@ -32,7 +32,7 @@ * @property Collection $questions * @property Collection $answers * @property Collection $assignedUsers - * @property Collection $quizSubmissions + * @property Collection $userQuizzes */ class Quiz extends Model { @@ -61,9 +61,9 @@ public function assignedUsers(): BelongsToMany return $this->belongsToMany(User::class, "quiz_assignments"); } - public function quizSubmissions(): HasMany + public function userQuizzes(): HasMany { - return $this->hasMany(QuizSubmission::class); + return $this->hasMany(UserQuiz::class); } public function isLocked(): Attribute @@ -124,22 +124,22 @@ public function clone(): self return $quizCopy; } - public function createSubmission(User $user): QuizSubmission + public function createUserQuiz(User $user): UserQuiz { - $submission = new QuizSubmission(); - $submission->closed_at = $this->closeAt; - $submission->quiz()->associate($this); - $submission->user()->associate($user); - $submission->save(); + $userQuiz = new UserQuiz(); + $userQuiz->closed_at = $this->closeAt; + $userQuiz->quiz()->associate($this); + $userQuiz->user()->associate($user); + $userQuiz->save(); foreach ($this->questions as $question) { - $answerRecord = new AnswerRecord(); - $answerRecord->quizSubmission()->associate($submission); - $answerRecord->question()->associate($question); - $answerRecord->save(); + $userQuestion = new UserQuestion(); + $userQuestion->userQuiz()->associate($userQuiz); + $userQuestion->question()->associate($question); + $userQuestion->save(); } - return $submission; + return $userQuiz; } public function isReadyToBePublished(): bool @@ -147,9 +147,9 @@ public function isReadyToBePublished(): bool return $this->scheduled_at !== null && $this->duration !== null && $this->allQuestionsHaveCorrectAnswer(); } - public function hasSubmissionsFrom(User $user): bool + public function hasUserQuizzesFrom(User $user): bool { - return $this->quizSubmissions->where("user_id", $user->id)->isNotEmpty(); + return $this->userQuizzes->where("user_id", $user->id)->isNotEmpty(); } protected function allQuestionsHaveCorrectAnswer(): bool diff --git a/app/Models/User.php b/app/Models/User.php index bf5aae8f..32ac52d4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,7 +20,7 @@ /** * @param string $token * @property int $id - * @property string $name + * @property string $firstname * @property string $surname * @property string $email * @property string $password @@ -30,7 +30,7 @@ * @property Carbon $updated_at * @property School $school * @property boolean $is_anonymized - * @property Collection $quizSubmissions + * @property Collection $userQuizzes * @property Collection $assignedQuizzes */ class User extends Authenticatable implements MustVerifyEmail, CanResetPassword @@ -40,7 +40,7 @@ class User extends Authenticatable implements MustVerifyEmail, CanResetPassword use HasRoles; protected $fillable = [ - "name", + "firstname", "surname", "email", "school_id", @@ -65,9 +65,9 @@ public function sendPasswordResetNotification($token): void $this->notify(new ResetPasswordNotification($token)); } - public function quizSubmissions(): HasMany + public function userQuizzes(): HasMany { - return $this->hasMany(QuizSubmission::class); + return $this->hasMany(UserQuiz::class); } public function assignedQuizzes() diff --git a/app/Models/AnswerRecord.php b/app/Models/UserQuestion.php similarity index 77% rename from app/Models/AnswerRecord.php rename to app/Models/UserQuestion.php index 35e862e8..9a0de9c3 100644 --- a/app/Models/AnswerRecord.php +++ b/app/Models/UserQuestion.php @@ -14,16 +14,16 @@ * @property int $id * @property Carbon $created_at * @property Carbon $updated_at - * @property int $quiz_submission_id + * @property int $user_quiz_id * @property int $question_id * @property int $answer_id * @property bool $isClosed * @property bool $isCorrect - * @property QuizSubmission $quizSubmission + * @property UserQuiz $userQuiz * @property Question $question * @property ?Answer $answer */ -class AnswerRecord extends Model +class UserQuestion extends Model { use HasFactory; @@ -31,9 +31,9 @@ class AnswerRecord extends Model "text", ]; - public function quizSubmission(): BelongsTo + public function userQuiz(): BelongsTo { - return $this->belongsTo(QuizSubmission::class); + return $this->belongsTo(UserQuiz::class); } public function answer(): BelongsTo @@ -48,7 +48,7 @@ public function question(): BelongsTo public function isClosed(): Attribute { - return Attribute::get(fn(): bool => $this->quizSubmission->isClosed); + return Attribute::get(fn(): bool => $this->userQuiz->isClosed); } public function isCorrect(): Attribute diff --git a/app/Models/QuizSubmission.php b/app/Models/UserQuiz.php similarity index 82% rename from app/Models/QuizSubmission.php rename to app/Models/UserQuiz.php index 681bf45f..e54473c1 100644 --- a/app/Models/QuizSubmission.php +++ b/app/Models/UserQuiz.php @@ -24,9 +24,9 @@ * @property bool $isClosed * @property Quiz $quiz * @property User $user - * @property Collection $answerRecords + * @property Collection $userQuestions */ -class QuizSubmission extends Model +class UserQuiz extends Model { use HasFactory; @@ -40,9 +40,9 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } - public function answerRecords(): HasMany + public function userQuestions(): HasMany { - return $this->hasMany(AnswerRecord::class); + return $this->hasMany(UserQuestion::class); } public function isClosed(): Attribute @@ -53,7 +53,7 @@ public function isClosed(): Attribute public function points(): Attribute { return Attribute::get(function (): int { - $correctAnswers = $this->answerRecords->filter(fn(AnswerRecord $record): bool => $record->isCorrect); + $correctAnswers = $this->userQuestions->filter(fn(UserQuestion $userQuestion): bool => $userQuestion->isCorrect); return $correctAnswers->count(); }); diff --git a/app/Policies/AnswerRecordPolicy.php b/app/Policies/AnswerRecordPolicy.php deleted file mode 100644 index 159446ab..00000000 --- a/app/Policies/AnswerRecordPolicy.php +++ /dev/null @@ -1,19 +0,0 @@ -quizSubmission; - - return !$submission->isClosed && $submission->user_id === $user->id && $answerRecord->question_id === $answer->question_id; - } -} diff --git a/app/Policies/QuizPolicy.php b/app/Policies/QuizPolicy.php index 43709907..15af4039 100644 --- a/app/Policies/QuizPolicy.php +++ b/app/Policies/QuizPolicy.php @@ -5,8 +5,8 @@ namespace App\Policies; use App\Models\Quiz; -use App\Models\QuizSubmission; use App\Models\User; +use App\Models\UserQuiz; use Illuminate\Auth\Access\Response; class QuizPolicy @@ -42,7 +42,7 @@ public function unlock(User $user, Quiz $quiz): bool public function assign(User $user, Quiz $quiz): bool { - return $quiz->isLocked && !$quiz->isPublished && !$quiz->hasSubmissionsFrom($user); + return $quiz->isLocked && !$quiz->isPublished && !$quiz->hasUserQuizzesFrom($user); } public function viewAdminRanking(User $user, Quiz $quiz): Response @@ -56,7 +56,7 @@ public function viewUserRanking(User $user, Quiz $quiz): Response return Response::deny("Ranking nie jest opublikowany."); } - $isUserInRanking = QuizSubmission::where("quiz_id", $quiz->id) + $isUserInRanking = UserQuiz::where("quiz_id", $quiz->id) ->where("user_id", $user->id) ->exists(); @@ -65,6 +65,6 @@ public function viewUserRanking(User $user, Quiz $quiz): Response public function publish(User $user, Quiz $quiz): bool { - return $quiz->isLocked && $user->hasRole("admin|super_admin") && $quiz->quizSubmissions->isNotEmpty(); + return $quiz->isLocked && $user->hasRole("admin|super_admin") && $quiz->userQuizzes->isNotEmpty(); } } diff --git a/app/Policies/QuizSubmissionPolicy.php b/app/Policies/QuizSubmissionPolicy.php deleted file mode 100644 index 8310e800..00000000 --- a/app/Policies/QuizSubmissionPolicy.php +++ /dev/null @@ -1,26 +0,0 @@ -id === $quizSubmission->user_id; - } - - public function close(User $user, QuizSubmission $quizSubmission): bool - { - return $user->id === $quizSubmission->user_id && !$quizSubmission->isClosed; - } - - public function result(User $user, QuizSubmission $quizSubmission): bool - { - return $user->id === $quizSubmission->user_id && $quizSubmission->isClosed; - } -} diff --git a/app/Policies/UserQuestionPolicy.php b/app/Policies/UserQuestionPolicy.php new file mode 100644 index 00000000..23a394e8 --- /dev/null +++ b/app/Policies/UserQuestionPolicy.php @@ -0,0 +1,19 @@ +userQuiz; + + return !$userQuiz->isClosed && $userQuiz->user_id === $user->id && $userQuestion->question_id === $answer->question_id; + } +} diff --git a/app/Policies/UserQuizPolicy.php b/app/Policies/UserQuizPolicy.php new file mode 100644 index 00000000..a15b0e23 --- /dev/null +++ b/app/Policies/UserQuizPolicy.php @@ -0,0 +1,26 @@ +id === $userQuiz->user_id; + } + + public function close(User $user, UserQuiz $userQuiz): bool + { + return $user->id === $userQuiz->user_id && !$userQuiz->isClosed; + } + + public function result(User $user, UserQuiz $userQuiz): bool + { + return $user->id === $userQuiz->user_id && $userQuiz->isClosed; + } +} diff --git a/app/Rules/Regon.php b/app/Rules/Regon.php new file mode 100644 index 00000000..b2899558 --- /dev/null +++ b/app/Rules/Regon.php @@ -0,0 +1,60 @@ +validate_short_regon($value, $fail); + + if (strlen($value) === 14) { + $this->validate_long_regon($value, $fail); + } + } + + protected function validate_short_regon(string $value, Closure $fail): void + { + $digits = str_split($value); + $checksum = RegonHelper::calculateChecksum($digits, RegonHelper::WEIGHTS_SHORT) % 11; + + if ($checksum !== intval($digits[8])) { + $fail(Lang::get("validation.custom.regon.invalid_checksum")); + } + } + + protected function validate_long_regon(string $value, Closure $fail): void + { + $digits = str_split($value); + + $checksum = RegonHelper::calculateChecksum($digits, RegonHelper::WEIGHTS_LONG) % 11; + + if ($checksum !== intval($digits[13])) { + $fail(Lang::get("validation.custom.regon.invalid_checksum")); + } + } +} diff --git a/app/Services/GetSchoolDataService.php b/app/Services/GetSchoolDataService.php index 692b23fd..f3f0761c 100644 --- a/app/Services/GetSchoolDataService.php +++ b/app/Services/GetSchoolDataService.php @@ -21,9 +21,9 @@ public function __construct( /** * @param array $schoolTypes */ - public function getSchools(Voivodeship $voivodeship, array $schoolTypes): void + public function getSchools(Voivodeship $voivodeship, array $schoolTypes): int { - $this->store($this->fetchSchools($voivodeship, $schoolTypes)); + return $this->store($this->fetchSchools($voivodeship, $schoolTypes)); } /** @@ -45,12 +45,17 @@ protected function fetchSchools(Voivodeship $voivodeship, array $schoolTypes): C /** * @param Collection $schools */ - protected function store(Collection $schools): void + protected function store(Collection $schools): int { + $fetched = 0; + foreach ($schools as $dto) { - $school = School::query()->firstOrNew(["regon" => $dto->regon]); - $school->fill($dto->toArray()); - $school->save(); + if (School::query()->where("regon", "=", $dto->regon)->doesntExist()) { + School::query()->create($dto->toArray()); + ++$fetched; + } } + + return $fetched; } } diff --git a/composer.lock b/composer.lock index 8926c8e4..2fe4ef9d 100644 --- a/composer.lock +++ b/composer.lock @@ -380,16 +380,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.3", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" + "reference": "8c784d071debd117328803d86b2097615b457500" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", "shasum": "" }, "require": { @@ -402,10 +402,14 @@ "require-dev": { "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { "Cron\\": "src/Cron/" @@ -429,7 +433,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" }, "funding": [ { @@ -437,7 +441,7 @@ "type": "github" } ], - "time": "2023-08-10T19:36:49+00:00" + "time": "2024-10-09T13:47:03+00:00" }, { "name": "egulias/email-validator", @@ -1128,16 +1132,16 @@ }, { "name": "laravel/framework", - "version": "v11.25.0", + "version": "v11.31.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b487a9089c0b1c71ac63bb6bc44fb4b00dc6da2e" + "reference": "365090ed2c68244e3141cdb5e247cdf3dfba2c40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b487a9089c0b1c71ac63bb6bc44fb4b00dc6da2e", - "reference": "b487a9089c0b1c71ac63bb6bc44fb4b00dc6da2e", + "url": "https://api.github.com/repos/laravel/framework/zipball/365090ed2c68244e3141cdb5e247cdf3dfba2c40", + "reference": "365090ed2c68244e3141cdb5e247cdf3dfba2c40", "shasum": "" }, "require": { @@ -1156,7 +1160,7 @@ "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.18|^0.2.0", + "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", @@ -1333,25 +1337,25 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-09-26T11:21:58+00:00" + "time": "2024-11-12T15:36:15+00:00" }, { "name": "laravel/prompts", - "version": "v0.2.1", + "version": "v0.3.2", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "a132ccf64d46da183b7cf3729df260e836cc7e15" + "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/a132ccf64d46da183b7cf3729df260e836cc7e15", - "reference": "a132ccf64d46da183b7cf3729df260e836cc7e15", + "url": "https://api.github.com/repos/laravel/prompts/zipball/0e0535747c6b8d6d10adca8b68293cf4517abb0f", + "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f", "shasum": "" }, "require": { + "composer-runtime-api": "^2.2", "ext-mbstring": "*", - "illuminate/collections": "^10.0|^11.0", "php": "^8.1", "symfony/console": "^6.2|^7.0" }, @@ -1360,8 +1364,9 @@ "laravel/framework": ">=10.17.0 <10.25.0" }, "require-dev": { + "illuminate/collections": "^10.0|^11.0", "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3", + "pestphp/pest": "^2.3|^3.4", "phpstan/phpstan": "^1.11", "phpstan/phpstan-mockery": "^1.1" }, @@ -1371,7 +1376,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.2.x-dev" + "dev-main": "0.3.x-dev" } }, "autoload": { @@ -1389,9 +1394,9 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.2.1" + "source": "https://github.com/laravel/prompts/tree/v0.3.2" }, - "time": "2024-09-19T10:28:37+00:00" + "time": "2024-11-12T14:59:47+00:00" }, { "name": "laravel/sanctum", @@ -1459,16 +1464,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v1.3.5", + "version": "v1.3.6", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c" + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f865a58ea3a0107c336b7045104c75243fa59d96", + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96", "shasum": "" }, "require": { @@ -1516,7 +1521,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-09-23T13:33:08+00:00" + "time": "2024-11-11T17:06:04+00:00" }, { "name": "laravel/tinker", @@ -1774,16 +1779,16 @@ }, { "name": "league/flysystem", - "version": "3.29.0", + "version": "3.29.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "0adc0d9a51852e170e0028a60bd271726626d3f0" + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0adc0d9a51852e170e0028a60bd271726626d3f0", - "reference": "0adc0d9a51852e170e0028a60bd271726626d3f0", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", "shasum": "" }, "require": { @@ -1851,9 +1856,9 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" }, - "time": "2024-09-29T11:59:11+00:00" + "time": "2024-10-08T08:58:34+00:00" }, { "name": "league/flysystem-local", @@ -1962,16 +1967,16 @@ }, { "name": "monolog/monolog", - "version": "3.7.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/32e515fdc02cdafbe4593e30a9350d486b125b67", + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67", "shasum": "" }, "require": { @@ -1991,12 +1996,14 @@ "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^10.5.17", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", "predis/predis": "^1.1 || ^2", - "ruflin/elastica": "^7", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -2047,7 +2054,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.7.0" + "source": "https://github.com/Seldaek/monolog/tree/3.8.0" }, "funding": [ { @@ -2059,24 +2066,24 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:40:51+00:00" + "time": "2024-11-12T13:57:08+00:00" }, { "name": "nesbot/carbon", - "version": "3.8.0", + "version": "3.8.2", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f" + "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f", - "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e1268cdbc486d97ce23fef2c666dc3c6b6de9947", + "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947", "shasum": "" }, "require": { - "carbonphp/carbon-doctrine-types": "*", + "carbonphp/carbon-doctrine-types": "<100.0", "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", @@ -2165,28 +2172,28 @@ "type": "tidelift" } ], - "time": "2024-08-19T06:22:39+00:00" + "time": "2024-11-07T17:46:48+00:00" }, { "name": "nette/schema", - "version": "v1.3.0", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188" + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", - "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.3" + "php": "8.1 - 8.4" }, "require-dev": { - "nette/tester": "^2.4", + "nette/tester": "^2.5.2", "phpstan/phpstan-nette": "^1.0", "tracy/tracy": "^2.8" }, @@ -2225,9 +2232,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.0" + "source": "https://github.com/nette/schema/tree/v1.3.2" }, - "time": "2023-12-11T11:54:22+00:00" + "time": "2024-10-06T23:10:23+00:00" }, { "name": "nette/utils", @@ -2375,32 +2382,31 @@ }, { "name": "nunomaduro/termwind", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a" + "reference": "42c84e4e8090766bbd6445d06cd6e57650626ea3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/e5f21eade88689536c0cdad4c3cd75f3ed26e01a", - "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/42c84e4e8090766bbd6445d06cd6e57650626ea3", + "reference": "42c84e4e8090766bbd6445d06cd6e57650626ea3", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.0.4" + "symfony/console": "^7.1.5" }, "require-dev": { - "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.1.1", - "laravel/pint": "^1.15.0", - "mockery/mockery": "^1.6.11", - "pestphp/pest": "^2.34.6", - "phpstan/phpstan": "^1.10.66", - "phpstan/phpstan-strict-rules": "^1.5.2", - "symfony/var-dumper": "^7.0.4", + "illuminate/console": "^11.28.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.5", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -2443,7 +2449,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.1.0" + "source": "https://github.com/nunomaduro/termwind/tree/v2.2.0" }, "funding": [ { @@ -2459,7 +2465,7 @@ "type": "github" } ], - "time": "2024-09-05T15:25:50+00:00" + "time": "2024-10-15T16:15:16+00:00" }, { "name": "phpoption/phpoption", @@ -3471,16 +3477,16 @@ }, { "name": "symfony/clock", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" + "reference": "97bebc53548684c17ed696bc8af016880f0f098d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", + "url": "https://api.github.com/repos/symfony/clock/zipball/97bebc53548684c17ed696bc8af016880f0f098d", + "reference": "97bebc53548684c17ed696bc8af016880f0f098d", "shasum": "" }, "require": { @@ -3525,7 +3531,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.1" + "source": "https://github.com/symfony/clock/tree/v7.1.6" }, "funding": [ { @@ -3541,20 +3547,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a", + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a", "shasum": "" }, "require": { @@ -3618,7 +3624,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.1.7" }, "funding": [ { @@ -3634,20 +3640,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-11-05T15:34:55+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", "shasum": "" }, "require": { @@ -3683,7 +3689,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + "source": "https://github.com/symfony/css-selector/tree/v7.1.6" }, "funding": [ { @@ -3699,7 +3705,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3770,16 +3776,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.1.3", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c" + "reference": "010e44661f4c6babaf8c4862fe68c24a53903342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/010e44661f4c6babaf8c4862fe68c24a53903342", + "reference": "010e44661f4c6babaf8c4862fe68c24a53903342", "shasum": "" }, "require": { @@ -3825,7 +3831,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.1.3" + "source": "https://github.com/symfony/error-handler/tree/v7.1.7" }, "funding": [ { @@ -3841,20 +3847,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T13:02:51+00:00" + "time": "2024-11-05T15:34:55+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + "reference": "87254c78dd50721cfd015b62277a8281c5589702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", + "reference": "87254c78dd50721cfd015b62277a8281c5589702", "shasum": "" }, "require": { @@ -3905,7 +3911,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" }, "funding": [ { @@ -3921,7 +3927,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4001,16 +4007,16 @@ }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -4045,7 +4051,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" }, "funding": [ { @@ -4061,20 +4067,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.1.5", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b" + "reference": "5183b61657807099d98f3367bcccb850238b17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e30ef73b1e44eea7eb37ba69600a354e553f694b", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5183b61657807099d98f3367bcccb850238b17a9", + "reference": "5183b61657807099d98f3367bcccb850238b17a9", "shasum": "" }, "require": { @@ -4122,7 +4128,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.1.5" + "source": "https://github.com/symfony/http-foundation/tree/v7.1.7" }, "funding": [ { @@ -4138,20 +4144,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-11-06T09:02:46+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.1.5", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b" + "reference": "7f137cda31fd41e422edcdc01915f2c095b84399" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/44204d96150a9df1fc57601ec933d23fefc2d65b", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7f137cda31fd41e422edcdc01915f2c095b84399", + "reference": "7f137cda31fd41e422edcdc01915f2c095b84399", "shasum": "" }, "require": { @@ -4236,7 +4242,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.1.5" + "source": "https://github.com/symfony/http-kernel/tree/v7.1.7" }, "funding": [ { @@ -4252,20 +4258,20 @@ "type": "tidelift" } ], - "time": "2024-09-21T06:09:21+00:00" + "time": "2024-11-06T09:54:34+00:00" }, { "name": "symfony/mailer", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b" + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/bbf21460c56f29810da3df3e206e38dfbb01e80b", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/69c9948451fb3a6a4d47dc8261d1794734e76cdd", + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd", "shasum": "" }, "require": { @@ -4316,7 +4322,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.5" + "source": "https://github.com/symfony/mailer/tree/v7.1.6" }, "funding": [ { @@ -4332,20 +4338,20 @@ "type": "tidelift" } ], - "time": "2024-09-08T12:32:26+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/mime", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff" + "reference": "caa1e521edb2650b8470918dfe51708c237f0598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/711d2e167e8ce65b05aea6b258c449671cdd38ff", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff", + "url": "https://api.github.com/repos/symfony/mime/zipball/caa1e521edb2650b8470918dfe51708c237f0598", + "reference": "caa1e521edb2650b8470918dfe51708c237f0598", "shasum": "" }, "require": { @@ -4400,7 +4406,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.5" + "source": "https://github.com/symfony/mime/tree/v7.1.6" }, "funding": [ { @@ -4416,7 +4422,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5056,16 +5062,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585", + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585", "shasum": "" }, "require": { @@ -5097,7 +5103,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.1.7" }, "funding": [ { @@ -5113,20 +5119,20 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-11-06T09:25:12+00:00" }, { "name": "symfony/routing", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7" + "reference": "66a2c469f6c22d08603235c46a20007c0701ea0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", + "url": "https://api.github.com/repos/symfony/routing/zipball/66a2c469f6c22d08603235c46a20007c0701ea0a", + "reference": "66a2c469f6c22d08603235c46a20007c0701ea0a", "shasum": "" }, "require": { @@ -5178,7 +5184,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.1.4" + "source": "https://github.com/symfony/routing/tree/v7.1.6" }, "funding": [ { @@ -5194,7 +5200,7 @@ "type": "tidelift" } ], - "time": "2024-08-29T08:16:25+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/service-contracts", @@ -5281,16 +5287,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -5348,7 +5354,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -5364,20 +5370,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/translation", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea" + "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/235535e3f84f3dfbdbde0208ede6ca75c3a489ea", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea", + "url": "https://api.github.com/repos/symfony/translation/zipball/b9f72ab14efdb6b772f85041fa12f820dee8d55f", + "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f", "shasum": "" }, "require": { @@ -5442,7 +5448,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.1.5" + "source": "https://github.com/symfony/translation/tree/v7.1.6" }, "funding": [ { @@ -5458,7 +5464,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T06:30:38+00:00" + "time": "2024-09-28T12:35:13+00:00" }, { "name": "symfony/translation-contracts", @@ -5540,16 +5546,16 @@ }, { "name": "symfony/uid", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317" + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/8c7bb8acb933964055215d89f9a9871df0239317", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317", + "url": "https://api.github.com/repos/symfony/uid/zipball/65befb3bb2d503bbffbd08c815aa38b472999917", + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917", "shasum": "" }, "require": { @@ -5594,7 +5600,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.5" + "source": "https://github.com/symfony/uid/tree/v7.1.6" }, "funding": [ { @@ -5610,20 +5616,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.1.5", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d" + "reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e20e03889539fd4e4211e14d2179226c513c010d", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f6ea51f669760cacd7464bf7eaa0be87b8072db1", + "reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1", "shasum": "" }, "require": { @@ -5677,7 +5683,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.5" + "source": "https://github.com/symfony/var-dumper/tree/v7.1.7" }, "funding": [ { @@ -5693,7 +5699,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T10:07:02+00:00" + "time": "2024-11-05T15:34:55+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -9934,13 +9940,13 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.3.4", "ext-pdo": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/database/factories/SchoolFactory.php b/database/factories/SchoolFactory.php index bc36c77f..0d121b9c 100644 --- a/database/factories/SchoolFactory.php +++ b/database/factories/SchoolFactory.php @@ -4,11 +4,10 @@ namespace Database\Factories; +use App\Helpers\RegonHelper; use App\Models\School; use Illuminate\Database\Eloquent\Factories\Factory; -use function str; - /** * @extends Factory */ @@ -23,12 +22,12 @@ public function definition(): array { return [ "name" => fake()->company(), - "regon" => str(fake()->randomNumber(9)), + "regon" => RegonHelper::generateShortRegon(), "city" => fake()->city(), "street" => fake()->streetName(), "building_number" => fake()->buildingNumber(), "apartment_number" => fake()->buildingNumber(), - "zip_code" => fake()->postcode(), + "zip_code" => fake()->randomNumber(2) . "-" . fake()->randomNumber(3), ]; } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 6c18b01e..15ac6060 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -19,7 +19,7 @@ class UserFactory extends Factory public function definition(): array { return [ - "name" => fake()->firstName(), + "firstname" => fake()->firstName(), "surname" => fake()->lastName(), "email" => fake()->unique()->safeEmail(), "email_verified_at" => Carbon::now(), diff --git a/database/factories/AnswerRecordFactory.php b/database/factories/UserQuestionFactory.php similarity index 58% rename from database/factories/AnswerRecordFactory.php rename to database/factories/UserQuestionFactory.php index d0ee29f6..33cfc8b9 100644 --- a/database/factories/AnswerRecordFactory.php +++ b/database/factories/UserQuestionFactory.php @@ -4,20 +4,20 @@ namespace Database\Factories; -use App\Models\AnswerRecord; use App\Models\Question; -use App\Models\QuizSubmission; +use App\Models\UserQuestion; +use App\Models\UserQuiz; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends Factory + * @extends Factory */ -class AnswerRecordFactory extends Factory +class UserQuestionFactory extends Factory { public function definition(): array { return [ - "quiz_submission_id" => QuizSubmission::factory(), + "user_quiz_id" => UserQuiz::factory(), "question_id" => Question::factory(), ]; } diff --git a/database/factories/QuizSubmissionFactory.php b/database/factories/UserQuizFactory.php similarity index 84% rename from database/factories/QuizSubmissionFactory.php rename to database/factories/UserQuizFactory.php index 4c10c093..e6f508e1 100644 --- a/database/factories/QuizSubmissionFactory.php +++ b/database/factories/UserQuizFactory.php @@ -5,15 +5,15 @@ namespace Database\Factories; use App\Models\Quiz; -use App\Models\QuizSubmission; use App\Models\User; +use App\Models\UserQuiz; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends Factory + * @extends Factory */ -class QuizSubmissionFactory extends Factory +class UserQuizFactory extends Factory { public function definition(): array { diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 38b52a4a..5f34af2f 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -11,7 +11,7 @@ public function up(): void { Schema::create("users", function (Blueprint $table): void { $table->bigIncrements("id")->unique(); - $table->string("name"); + $table->string("firstname"); $table->string("surname"); $table->string("email")->unique(); $table->timestamp("email_verified_at")->nullable(); diff --git a/database/migrations/2024_08_13_104625_create_quiz_submissions_table.php b/database/migrations/2024_08_13_104625_create_quiz_submissions_table.php index 59639aa2..4783a7dd 100644 --- a/database/migrations/2024_08_13_104625_create_quiz_submissions_table.php +++ b/database/migrations/2024_08_13_104625_create_quiz_submissions_table.php @@ -11,7 +11,7 @@ return new class() extends Migration { public function up(): void { - Schema::create("quiz_submissions", function (Blueprint $table): void { + Schema::create("user_quizzes", function (Blueprint $table): void { $table->id(); $table->foreignIdFor(Quiz::class)->constrained()->cascadeOnDelete(); $table->foreignIdFor(User::class)->constrained()->cascadeOnDelete(); @@ -22,6 +22,6 @@ public function up(): void public function down(): void { - Schema::dropIfExists("quiz_submissions"); + Schema::dropIfExists("user_quizzes"); } }; diff --git a/database/migrations/2024_08_13_104743_create_answer_records_table.php b/database/migrations/2024_08_13_104743_create_answer_records_table.php index e026e30d..3e74d73f 100644 --- a/database/migrations/2024_08_13_104743_create_answer_records_table.php +++ b/database/migrations/2024_08_13_104743_create_answer_records_table.php @@ -4,7 +4,7 @@ use App\Models\Answer; use App\Models\Question; -use App\Models\QuizSubmission; +use App\Models\UserQuiz; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -12,9 +12,9 @@ return new class() extends Migration { public function up(): void { - Schema::create("answer_records", function (Blueprint $table): void { + Schema::create("user_questions", function (Blueprint $table): void { $table->id(); - $table->foreignIdFor(QuizSubmission::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(UserQuiz::class)->constrained()->cascadeOnDelete(); $table->foreignIdFor(Question::class)->constrained()->cascadeOnDelete(); $table->foreignIdFor(Answer::class)->nullable()->constrained()->nullOnDelete(); $table->timestamps(); @@ -23,6 +23,6 @@ public function up(): void public function down(): void { - Schema::dropIfExists("answer_records"); + Schema::dropIfExists("user_questions"); } }; diff --git a/database/seeders/AdminSeeder.php b/database/seeders/AdminSeeder.php index ccec082c..44cea50a 100644 --- a/database/seeders/AdminSeeder.php +++ b/database/seeders/AdminSeeder.php @@ -18,7 +18,7 @@ public function run(): void $superAdmin = User::firstOrCreate( ["email" => "superadmin@example.com"], [ - "name" => "Example", + "firstname" => "Example", "surname" => "Super Admin", "email_verified_at" => Carbon::now(), "password" => Hash::make("password"), @@ -31,7 +31,7 @@ public function run(): void $admin = User::firstOrCreate( ["email" => "admin@example.com"], [ - "name" => "Example", + "firstname" => "Example", "surname" => "Admin", "email_verified_at" => Carbon::now(), "password" => Hash::make("password"), @@ -44,7 +44,7 @@ public function run(): void $user = User::firstOrCreate( ["email" => "user@example.com"], [ - "name" => "Example", + "firstname" => "Example", "surname" => "User", "email_verified_at" => Carbon::now(), "password" => Hash::make("password"), diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 33bd9207..e630fe40 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -36,9 +36,9 @@ public function run(): void } foreach (User::query()->role("user")->get() as $user) { - $submission = $quiz->createSubmission($user); + $userQuiz = $quiz->createUserQuiz($user); - foreach ($submission->answerRecords as $answer) { + foreach ($userQuiz->userQuestions as $answer) { $answer->answer()->associate($answer->question->answers->random()); $answer->save(); } diff --git a/database/seeders/UserQuizSeeder.php b/database/seeders/UserQuizSeeder.php index b0705f85..fd0c6e34 100644 --- a/database/seeders/UserQuizSeeder.php +++ b/database/seeders/UserQuizSeeder.php @@ -5,11 +5,11 @@ namespace Database\Seeders; use App\Models\Answer; -use App\Models\AnswerRecord; use App\Models\Question; use App\Models\Quiz; -use App\Models\QuizSubmission; use App\Models\User; +use App\Models\UserQuestion; +use App\Models\UserQuiz; use Illuminate\Database\Seeder; class UserQuizSeeder extends Seeder @@ -42,13 +42,13 @@ public function run(): void } } - $this->createSubmissionForUser($user1, null); - $this->createSubmissionForUser($user2, null); + $this->createUserQuizForUser($user1, null); + $this->createUserQuizForUser($user2, null); } - public function createSubmissionForUser(User $user, ?int $correctAnswersCount): void + public function createUserQuizForUser(User $user, ?int $correctAnswersCount): void { - $quizSubmission = QuizSubmission::factory() + $userQuiz = UserQuiz::factory() ->for($this->quiz) ->for($user) ->create(); @@ -71,8 +71,8 @@ public function createSubmissionForUser(User $user, ?int $correctAnswersCount): ? $answers->where("id", $question->correct_answer_id)->first() : $answers->where("id", "!=", $question->correct_answer_id)->random(); - AnswerRecord::factory() - ->for($quizSubmission) + UserQuestion::factory() + ->for($userQuiz) ->for($question) ->for($selectedAnswer) ->create(); diff --git a/lang/en/validation.php b/lang/en/validation.php index a9fb9422..90d6575f 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -154,6 +154,11 @@ "attribute-name" => [ "rule-name" => "custom-message", ], + "regon" => [ + "digits_only" => "The :attribute must contain only digits.", + "invalid_length" => "The :attribute must be 9 or 14 digits long.", + "invalid_checksum" => "The :attribute checksum is incorrect for a valid REGON.", + ], ], "attributes" => [], ]; diff --git a/lang/pl/validation.php b/lang/pl/validation.php index c690cb35..0ebd5cde 100644 --- a/lang/pl/validation.php +++ b/lang/pl/validation.php @@ -131,9 +131,18 @@ "questions.*.text" => [ "required" => "Treść pytania jest wymagana.", ], + "regon" => [ + "digits_only" => "Pole :attribute może zawierać tylko cyfry.", + "invalid_length" => "Pole :attribute musi mieć dokładnie 9 lub 14 cyfr.", + "invalid_checksum" => "Pole :attribute ma nieprawidłową sumę kontrolną.", + ], + "sorting" => [ + "unsupported_field" => "Sortowanie po polu :attribute nie jest wspierane.", + ], ], "attributes" => [ - "name" => "imię", + "name" => "nazwa", + "firstname" => "imię", "title" => "tytuł", "duration" => "czas trwania", "scheduled_at" => "rozpoczęcie", @@ -142,5 +151,11 @@ "date" => "data", "password" => "hasło", "school_id" => "szkoła", + "regon" => "regon", + "street" => "ulica", + "building_number" => "numer budynku", + "apartment_number" => "numer mieszkania", + "zip_code" => "kod pocztowy", + "city" => "miasto", ], ]; diff --git a/public/favicon.png b/public/favicon.png index c92ba5cc..f458cb32 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/public/favicon.svg b/public/favicon.svg index 385cb539..6dd12379 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,4 +1,4 @@ - - + + diff --git a/public/logo.png b/public/logo.png index 4b93f66c..2f8a1ba3 100644 Binary files a/public/logo.png and b/public/logo.png differ diff --git a/resources/css/app.css b/resources/css/app.css index 0a3647ec..5ab975a0 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -7,6 +7,18 @@ font-family: 'Poppins', sans-serif; } +.theme-witelon { + --color-primary: 38 44 137; + --color-primary-bright: 37 43 174; + --color-primary-dark: 20 22 71; +} + +.theme-tauron { + --color-primary: 228 0 125; + --color-primary-bright: 245 13 147; + --color-primary-dark: 176 4 96; +} + .v-enter-active, .v-leave-active { transition: opacity 0.3s ease; @@ -27,7 +39,7 @@ } ::-webkit-scrollbar-thumb { - background: #262c8926; + background: #E4007D26; border-radius: 1rem; border: 2px solid transparent; background-clip: content-box; @@ -35,7 +47,7 @@ } ::-webkit-scrollbar-thumb:hover { - background: #262c894c; + background: #E4007D4c; border: 2px solid transparent; background-clip: content-box; position: fixed; diff --git a/resources/js/Helpers/Converter.ts b/resources/js/Helpers/Converter.ts new file mode 100644 index 00000000..6966eaea --- /dev/null +++ b/resources/js/Helpers/Converter.ts @@ -0,0 +1,29 @@ +import dayjs from 'dayjs' +import { formatDate } from '@/Helpers/Format' + +export const converter = { + question: { + adminToUser(question: Question): UserQuestion { + return { + id: 1, + text: question.text ?? '', + answers: question.answers as UserAnswer[], + } + }, + }, + quiz: { + adminToUser(quiz: Quiz): UserQuiz { + return { + title: quiz.title, + quiz: quiz.id, + closedAt: formatDate( + dayjs(quiz.scheduledAt).add(quiz.duration ?? 0, 'minute'), + false, + ), + questions: quiz.questions.map( + converter.question.adminToUser, + ), + } + }, + }, +} diff --git a/resources/js/Helpers/KeysManager.ts b/resources/js/Helpers/KeysManager.ts index 1090e02f..2692c7b1 100644 --- a/resources/js/Helpers/KeysManager.ts +++ b/resources/js/Helpers/KeysManager.ts @@ -9,9 +9,9 @@ export function keysWrapper(objects: T[]): Arr return objects as Array } -const itemsAsKeys = new WeakMap() +const itemsAsKeys = new WeakMap() -export default function getKey(item: any) : string{ +export default function getKey(item: object) : string{ if (!itemsAsKeys.has(item)) itemsAsKeys.set(item, nanoid()) return itemsAsKeys.get(item) ?? nanoid() diff --git a/resources/js/Helpers/Params.ts b/resources/js/Helpers/Params.ts new file mode 100644 index 00000000..7483c8c3 --- /dev/null +++ b/resources/js/Helpers/Params.ts @@ -0,0 +1,10 @@ +export function useParams(): Record { + const searchParams = new URL(window.location.href).searchParams + let params: Record = {} + + for (const [key, value] of searchParams.entries()) { + params[key] = value + } + + return params +} diff --git a/resources/js/Helpers/Sorter.ts b/resources/js/Helpers/Sorter.ts index 8750c0f3..61d3679d 100644 --- a/resources/js/Helpers/Sorter.ts +++ b/resources/js/Helpers/Sorter.ts @@ -1,48 +1,43 @@ -import {onMounted, ref, type Ref, watch} from 'vue' -import dayjs from 'dayjs' -import {keysWrapper} from '@/Helpers/KeysManager' +import {computed, type Ref, ref, watch} from 'vue' +import {router} from '@inertiajs/vue3' +import {useParams} from '@/Helpers/Params' -function setSorter(sorter: Ref | undefined>, sorterName: string, type: 'name' | 'title' | 'creationDate' | 'modificationDate', desc = false) { - sessionStorage.setItem(`${sorterName}SorterPreference`, JSON.stringify([type, desc])) +export function useSorter(sortOptions: SortOption[], searchText?: Ref, customQueries?: () => string[]): [query: Ref, Ref] { + const params = useParams() + const key = ref(params.sort) + const desc = ref(params.order) - sorter.value = (a: T, b: T) => { - if (desc) { - [a, b] = [b, a] + const options = computed(() => sortOptions.map