From 1ce003ed302d76a95fab031ee385bc46494b671b Mon Sep 17 00:00:00 2001 From: DenysTyndyk Date: Mon, 27 Apr 2026 11:17:27 +0200 Subject: [PATCH] feat: add nuc_entities_structural module --- atomic/atom/icon/index.tsx | 11 +- .../atomic/template/entity-chart/index.tsx | 36 +-- modules/nuc_dialog/types/interfaces.ts | 4 +- modules/nuc_entities_structural/README.md | 11 + .../app/Contracts/QuestionContract.php | 24 ++ .../app/Contracts/TechnologyContract.php | 24 ++ .../Http/Controllers/QuestionController.php | 142 +++++++++++ .../Http/Controllers/StructuralController.php | 22 ++ .../Http/Controllers/TechnologyController.php | 131 +++++++++++ .../Http/Requests/Question/PostRequest.php | 34 +++ .../app/Http/Requests/Question/PutRequest.php | 35 +++ .../Http/Requests/Technology/PostRequest.php | 34 +++ .../Http/Requests/Technology/PutRequest.php | 35 +++ .../app/Models/Question.php | 147 ++++++++++++ .../app/Models/Technology.php | 147 ++++++++++++ .../app/Resources/QuestionResource.php | 29 +++ .../app/Resources/TechnologyResource.php | 29 +++ .../app/Services/QuestionService.php | 197 ++++++++++++++++ .../app/Services/TechnologyService.php | 176 ++++++++++++++ .../atomic/bosons/constants/fields/index.ts | 2 + .../bosons/constants/fields/question.ts | 39 ++++ .../bosons/constants/fields/technology.ts | 43 ++++ .../atomic/bosons/constants/index.ts | 1 + .../atomic/bosons/index.ts | 3 + .../atomic/bosons/types/api/Question/index.ts | 1 + .../bosons/types/api/Question/interfaces.ts | 34 +++ .../bosons/types/api/Technology/index.ts | 1 + .../bosons/types/api/Technology/interfaces.ts | 28 +++ .../atomic/bosons/types/api/index.ts | 2 + .../atomic/bosons/types/index.ts | 2 + .../bosons/types/object/Question/index.ts | 1 + .../types/object/Question/interfaces.ts | 11 + .../bosons/types/object/Technology/index.ts | 1 + .../types/object/Technology/interfaces.ts | 12 + .../atomic/bosons/types/object/index.ts | 2 + .../atomic/bosons/utils/api/index.ts | 2 + .../bosons/utils/api/question_requests.ts | 155 ++++++++++++ .../bosons/utils/api/technology_requests.ts | 138 +++++++++++ .../atomic/bosons/utils/index.ts | 1 + .../nuc_entities_structural/atomic/index.ts | 3 + .../atomic/pages/General/component.tsx | 120 ++++++++++ .../atomic/pages/General/index.ts | 1 + .../atomic/pages/Question/component.tsx | 37 +++ .../atomic/pages/Question/index.ts | 1 + .../atomic/pages/Technology/component.tsx | 37 +++ .../atomic/pages/Technology/index.ts | 1 + .../atomic/pages/index.ts | 3 + .../atomic/templates/Dashboard/Question.tsx | 164 +++++++++++++ .../atomic/templates/Dashboard/Technology.tsx | 167 +++++++++++++ .../atomic/templates/Dashboard/index.ts | 2 + .../atomic/templates/index.ts | 1 + modules/nuc_entities_structural/config.json | 8 + .../database/constants/Questions/en/About.php | 28 +++ .../database/constants/Questions/en/Home.php | 32 +++ .../database/constants/Questions/en/Offer.php | 40 ++++ .../constants/Questions/en/Services.php | 24 ++ .../database/constants/Questions/pl/About.php | 28 +++ .../database/constants/Questions/pl/Home.php | 32 +++ .../database/constants/Questions/pl/Offer.php | 40 ++++ .../constants/Questions/pl/Services.php | 24 ++ .../constants/Technologies/General.php | 124 ++++++++++ .../database/factories/QuestionFactory.php | 44 ++++ .../database/factories/TechnologyFactory.php | 42 ++++ ...25_01_13_135628_create_questions_table.php | 34 +++ ...02_22_235904_create_technologies_table.php | 33 +++ .../seeders/EntitiesStructuralSeeder.php | 16 ++ .../database/seeders/QuestionSeeder.php | 34 +++ .../database/seeders/TechnologySeeder.php | 26 +++ modules/nuc_entities_structural/index.ts | 14 ++ .../nuc_entities_structural.php | 14 ++ .../nuc_entities_structural.ts | 20 ++ .../nuc_entities_structural/routes/api.php | 60 +++++ .../Factories/QuestionFactoryTest.php | 53 +++++ .../Factories/TechnologyFactoryTest.php | 53 +++++ .../Migrations/QuestionMigrationsTest.php | 32 +++ .../Migrations/TechnologyMigrationsTest.php | 32 +++ .../tests/Database/Models/QuestionTest.php | 130 +++++++++++ .../tests/Database/Models/TechnologyTest.php | 130 +++++++++++ .../Feature/Api/Question/HTTP200Test.php | 72 ++++++ .../Feature/Api/Question/HTTP302Test.php | 39 ++++ .../Feature/Api/Question/HTTP401Test.php | 77 ++++++ .../Feature/Api/Question/HTTP405AuthTest.php | 96 ++++++++ .../Api/Question/HTTP405UnAuthTest.php | 91 ++++++++ .../Feature/Api/Question/HTTP422PostTest.php | 146 ++++++++++++ .../Feature/Api/Question/HTTP422PutTest.php | 129 ++++++++++ .../Feature/Api/Question/HTTP500Test.php | 80 +++++++ .../Feature/Api/Technology/HTTP200Test.php | 72 ++++++ .../Feature/Api/Technology/HTTP302Test.php | 39 ++++ .../Feature/Api/Technology/HTTP401Test.php | 77 ++++++ .../Api/Technology/HTTP405AuthTest.php | 96 ++++++++ .../Api/Technology/HTTP405UnAuthTest.php | 91 ++++++++ .../Api/Technology/HTTP422PostTest.php | 197 ++++++++++++++++ .../Feature/Api/Technology/HTTP422PutTest.php | 221 ++++++++++++++++++ .../Feature/Api/Technology/HTTP500Test.php | 80 +++++++ .../Controllers/QuestionControllerTest.php | 120 ++++++++++ .../Controllers/TechnologyControllerTest.php | 120 ++++++++++ .../nuc_entities_structural/tests/Pest.php | 13 ++ .../tests/TestConstants.php | 68 ++++++ .../tests/TestGroups.php | 82 +++++++ .../tests/TestUses.php | 57 +++++ .../vitests/api/Question/200.test.ts | 70 ++++++ .../vitests/api/Technology/200.test.ts | 59 +++++ .../vitests/constants/api/index.ts | 2 + .../vitests/constants/api/question.ts | 11 + .../vitests/constants/api/technology.ts | 11 + .../vitests/constants/index.ts | 1 + .../nuc_entities_structural/vitests/index.ts | 1 + 107 files changed, 5630 insertions(+), 19 deletions(-) create mode 100644 modules/nuc_entities_structural/README.md create mode 100644 modules/nuc_entities_structural/app/Contracts/QuestionContract.php create mode 100644 modules/nuc_entities_structural/app/Contracts/TechnologyContract.php create mode 100644 modules/nuc_entities_structural/app/Http/Controllers/QuestionController.php create mode 100644 modules/nuc_entities_structural/app/Http/Controllers/StructuralController.php create mode 100644 modules/nuc_entities_structural/app/Http/Controllers/TechnologyController.php create mode 100644 modules/nuc_entities_structural/app/Http/Requests/Question/PostRequest.php create mode 100644 modules/nuc_entities_structural/app/Http/Requests/Question/PutRequest.php create mode 100644 modules/nuc_entities_structural/app/Http/Requests/Technology/PostRequest.php create mode 100644 modules/nuc_entities_structural/app/Http/Requests/Technology/PutRequest.php create mode 100644 modules/nuc_entities_structural/app/Models/Question.php create mode 100644 modules/nuc_entities_structural/app/Models/Technology.php create mode 100644 modules/nuc_entities_structural/app/Resources/QuestionResource.php create mode 100644 modules/nuc_entities_structural/app/Resources/TechnologyResource.php create mode 100644 modules/nuc_entities_structural/app/Services/QuestionService.php create mode 100644 modules/nuc_entities_structural/app/Services/TechnologyService.php create mode 100644 modules/nuc_entities_structural/atomic/bosons/constants/fields/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/constants/fields/question.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/constants/fields/technology.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/constants/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/api/Question/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/api/Question/interfaces.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/api/Technology/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/api/Technology/interfaces.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/api/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/object/Question/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/object/Question/interfaces.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/object/Technology/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/object/Technology/interfaces.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/types/object/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/utils/api/index.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/utils/api/question_requests.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/utils/api/technology_requests.ts create mode 100644 modules/nuc_entities_structural/atomic/bosons/utils/index.ts create mode 100644 modules/nuc_entities_structural/atomic/index.ts create mode 100644 modules/nuc_entities_structural/atomic/pages/General/component.tsx create mode 100644 modules/nuc_entities_structural/atomic/pages/General/index.ts create mode 100644 modules/nuc_entities_structural/atomic/pages/Question/component.tsx create mode 100644 modules/nuc_entities_structural/atomic/pages/Question/index.ts create mode 100644 modules/nuc_entities_structural/atomic/pages/Technology/component.tsx create mode 100644 modules/nuc_entities_structural/atomic/pages/Technology/index.ts create mode 100644 modules/nuc_entities_structural/atomic/pages/index.ts create mode 100644 modules/nuc_entities_structural/atomic/templates/Dashboard/Question.tsx create mode 100644 modules/nuc_entities_structural/atomic/templates/Dashboard/Technology.tsx create mode 100644 modules/nuc_entities_structural/atomic/templates/Dashboard/index.ts create mode 100644 modules/nuc_entities_structural/atomic/templates/index.ts create mode 100644 modules/nuc_entities_structural/config.json create mode 100644 modules/nuc_entities_structural/database/constants/Questions/en/About.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/en/Home.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/en/Offer.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/en/Services.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/pl/About.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/pl/Home.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/pl/Offer.php create mode 100644 modules/nuc_entities_structural/database/constants/Questions/pl/Services.php create mode 100644 modules/nuc_entities_structural/database/constants/Technologies/General.php create mode 100644 modules/nuc_entities_structural/database/factories/QuestionFactory.php create mode 100644 modules/nuc_entities_structural/database/factories/TechnologyFactory.php create mode 100644 modules/nuc_entities_structural/database/migrations/2025_01_13_135628_create_questions_table.php create mode 100644 modules/nuc_entities_structural/database/migrations/2025_02_22_235904_create_technologies_table.php create mode 100644 modules/nuc_entities_structural/database/seeders/EntitiesStructuralSeeder.php create mode 100644 modules/nuc_entities_structural/database/seeders/QuestionSeeder.php create mode 100644 modules/nuc_entities_structural/database/seeders/TechnologySeeder.php create mode 100644 modules/nuc_entities_structural/index.ts create mode 100644 modules/nuc_entities_structural/nuc_entities_structural.php create mode 100644 modules/nuc_entities_structural/nuc_entities_structural.ts create mode 100644 modules/nuc_entities_structural/routes/api.php create mode 100644 modules/nuc_entities_structural/tests/Database/Factories/QuestionFactoryTest.php create mode 100644 modules/nuc_entities_structural/tests/Database/Factories/TechnologyFactoryTest.php create mode 100644 modules/nuc_entities_structural/tests/Database/Migrations/QuestionMigrationsTest.php create mode 100644 modules/nuc_entities_structural/tests/Database/Migrations/TechnologyMigrationsTest.php create mode 100644 modules/nuc_entities_structural/tests/Database/Models/QuestionTest.php create mode 100644 modules/nuc_entities_structural/tests/Database/Models/TechnologyTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP200Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP302Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP401Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405AuthTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405UnAuthTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PostTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PutTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP500Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP200Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP302Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP401Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405AuthTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405UnAuthTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PostTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PutTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP500Test.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Controllers/QuestionControllerTest.php create mode 100644 modules/nuc_entities_structural/tests/Feature/Controllers/TechnologyControllerTest.php create mode 100644 modules/nuc_entities_structural/tests/Pest.php create mode 100644 modules/nuc_entities_structural/tests/TestConstants.php create mode 100644 modules/nuc_entities_structural/tests/TestGroups.php create mode 100644 modules/nuc_entities_structural/tests/TestUses.php create mode 100644 modules/nuc_entities_structural/vitests/api/Question/200.test.ts create mode 100644 modules/nuc_entities_structural/vitests/api/Technology/200.test.ts create mode 100644 modules/nuc_entities_structural/vitests/constants/api/index.ts create mode 100644 modules/nuc_entities_structural/vitests/constants/api/question.ts create mode 100644 modules/nuc_entities_structural/vitests/constants/api/technology.ts create mode 100644 modules/nuc_entities_structural/vitests/constants/index.ts create mode 100644 modules/nuc_entities_structural/vitests/index.ts diff --git a/atomic/atom/icon/index.tsx b/atomic/atom/icon/index.tsx index 2898853..2a90b2b 100644 --- a/atomic/atom/icon/index.tsx +++ b/atomic/atom/icon/index.tsx @@ -9,6 +9,7 @@ export function AdIcon({ size, className = '', style, + adType, ...rest }: IconInterface & { style?: CSSProperties }): JSX.Element | null { if (!icon) return null @@ -28,6 +29,7 @@ export function AdIcon({ icon={icon} className={cx(styles['iconify-icon'], className)} style={mergedStyle} + {...(adType ? { 'data-ad-type': adType } : {})} {...rest} /> ) @@ -36,5 +38,12 @@ export function AdIcon({ const iconClass = `pi pi-${icon.replace('prime:', '')}` const mergedClassName = cx(iconClass, styles['prime-icon'], className) - return + return ( + + ) } diff --git a/modules/nuc_charts/atomic/template/entity-chart/index.tsx b/modules/nuc_charts/atomic/template/entity-chart/index.tsx index caf2e83..2a2cd0a 100644 --- a/modules/nuc_charts/atomic/template/entity-chart/index.tsx +++ b/modules/nuc_charts/atomic/template/entity-chart/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { ChartData } from 'chart.js' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useMemo } from 'react' import { AdChart, type ChartType } from 'nucleify' @@ -10,17 +10,15 @@ import { useChart } from './utils/use_chart' export const NucEntityChart: React.FC = (props) => { const { setChartData, setChartOptions } = useChart() - const [chartData, setChartDataState] = useState(null) - const chartOptions = useMemo(() => { if (!props.type) return {} return setChartOptions(props.type as ChartType, props.direction) }, [props.type, props.direction, setChartOptions]) - useEffect(() => { - const updateData = () => { - const dataToSet = setChartData( + const chartData: ChartData | undefined = useMemo(() => { + return ( + setChartData( props.chartMethodType, props.data?.activity, props.data?.article, @@ -31,19 +29,25 @@ export const NucEntityChart: React.FC = (props) => { props.data?.technology, props.data?.user, props.example - ) - - if (dataToSet) { - setChartDataState(dataToSet) - } - } - - updateData() - }, [props.chartMethodType, props.example, props.data, setChartData]) + ) ?? undefined + ) + }, [ + props.chartMethodType, + props.data?.activity, + props.data?.article, + props.data?.contact, + props.data?.file, + props.data?.money, + props.data?.question, + props.data?.technology, + props.data?.user, + props.example, + setChartData, + ]) return (   nuc_entities_structural + +Module for all structural entities in Nucleify. + +
+ +

    Contributors


+ + + + diff --git a/modules/nuc_entities_structural/app/Contracts/QuestionContract.php b/modules/nuc_entities_structural/app/Contracts/QuestionContract.php new file mode 100644 index 0000000..74015d2 --- /dev/null +++ b/modules/nuc_entities_structural/app/Contracts/QuestionContract.php @@ -0,0 +1,24 @@ +service = $service; + } + + /** + * Show the application dashboard. + */ + public function render(): Renderable + { + return view('questions'); + } + + public function index(Request $request): JsonResponse + { + try { + $result = $this->service->index($request); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function countByCreatedLastWeek(Request $request): JsonResponse + { + try { + $result = $this->service->countByCreatedLastWeek($request); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function getByCategory(string $category): JsonResponse + { + try { + $result = $this->service->getByCategory($category); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function getSiteQuestions(string $site): JsonResponse + { + try { + $result = $this->service->getSiteQuestions($site); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function getSiteQuestionsByLang(string $site, string $lang): JsonResponse + { + try { + $result = $this->service->getSiteQuestionsByLang($site, $lang); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function show($id): JsonResponse + { + try { + $result = $this->service->show($id); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function store(PostRequest $request): JsonResponse + { + try { + $input = $request->validated(); + $result = $this->service->create($input); + + return response()->json([ + $result, + 'message' => 'Successfully created: ' . $result['content'] . ' question', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function update(PutRequest $request, $id): JsonResponse + { + try { + $input = $request->validated(); + $result = $this->service->update($id, $input); + + return response()->json([ + $result, + 'message' => 'Successfully updated: ' . $result['content'] . ' question', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function destroy($id): JsonResponse + { + $result = Question::findOrFail($id); + + try { + $this->service->delete($id); + + return response()->json([ + 'deleted' => true, + 'message' => 'Successfully deleted: ' . $result->getContent() . ' question', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } +} diff --git a/modules/nuc_entities_structural/app/Http/Controllers/StructuralController.php b/modules/nuc_entities_structural/app/Http/Controllers/StructuralController.php new file mode 100644 index 0000000..570c51e --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Controllers/StructuralController.php @@ -0,0 +1,22 @@ +exists($viewName)) { + abort(404, 'View not found.'); + } + + return view($viewName); + } +} diff --git a/modules/nuc_entities_structural/app/Http/Controllers/TechnologyController.php b/modules/nuc_entities_structural/app/Http/Controllers/TechnologyController.php new file mode 100644 index 0000000..5c6c1c1 --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Controllers/TechnologyController.php @@ -0,0 +1,131 @@ +service = $service; + } + + /** + * Show the application dashboard. + */ + public function render(): Renderable + { + return view('technologies'); + } + + public function index(Request $request): JsonResponse + { + try { + $result = $this->service->index($request); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function countByCreatedLastWeek(Request $request): JsonResponse + { + try { + $result = $this->service->countByCreatedLastWeek($request); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function getByCategory(string $category): JsonResponse + { + try { + $result = $this->service->getByCategory($category); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function getSiteTechnologies(string $site): JsonResponse + { + try { + $result = $this->service->getSiteTechnologies($site); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function show($id): JsonResponse + { + try { + $result = $this->service->show($id); + + return response()->json($result); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function store(PostRequest $request): JsonResponse + { + try { + $input = $request->validated(); + $result = $this->service->create($input); + + return response()->json([ + $result, + 'message' => 'Successfully created: ' . $result['label'] . ' technology', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function update(PutRequest $request, $id): JsonResponse + { + try { + $input = $request->validated(); + $result = $this->service->update($id, $input); + + return response()->json([ + $result, + 'message' => 'Successfully updated: ' . $result['label'] . ' technology', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + public function destroy($id): JsonResponse + { + $result = Technology::findOrFail($id); + + try { + $this->service->delete($id); + + return response()->json([ + 'deleted' => true, + 'message' => 'Successfully deleted: ' . $result->getLabel() . ' technology', + ]); + } catch (Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } +} diff --git a/modules/nuc_entities_structural/app/Http/Requests/Question/PostRequest.php b/modules/nuc_entities_structural/app/Http/Requests/Question/PostRequest.php new file mode 100644 index 0000000..f6bd545 --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Requests/Question/PostRequest.php @@ -0,0 +1,34 @@ + + */ + public function rules(): array + { + return [ + 'index' => 'required|integer', + 'content' => 'required|string|max:255', + 'answer' => 'required|string|max:1000', + 'category' => 'string|max:255', + 'on_site' => 'bool', + 'display' => 'bool', + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Http/Requests/Question/PutRequest.php b/modules/nuc_entities_structural/app/Http/Requests/Question/PutRequest.php new file mode 100644 index 0000000..f5d75ab --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Requests/Question/PutRequest.php @@ -0,0 +1,35 @@ + + */ + public function rules(): array + { + return [ + 'index' => 'required|integer', + 'content' => 'required|string|max:255', + 'answer' => 'required|string|max:1000', + 'category' => 'string|max:255', + 'on_site' => 'bool', + 'display' => 'bool', + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Http/Requests/Technology/PostRequest.php b/modules/nuc_entities_structural/app/Http/Requests/Technology/PostRequest.php new file mode 100644 index 0000000..ada173b --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Requests/Technology/PostRequest.php @@ -0,0 +1,34 @@ + + */ + public function rules(): array + { + return [ + 'label' => 'required|string|max:50', + 'description' => 'string|min:3|max:255', + 'href' => 'required|string', + 'src' => 'required|string', + 'category' => 'string', + 'display' => 'required|bool', + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Http/Requests/Technology/PutRequest.php b/modules/nuc_entities_structural/app/Http/Requests/Technology/PutRequest.php new file mode 100644 index 0000000..afb5c3b --- /dev/null +++ b/modules/nuc_entities_structural/app/Http/Requests/Technology/PutRequest.php @@ -0,0 +1,35 @@ + + */ + public function rules(): array + { + return [ + 'label' => 'required|string|max:50', + 'description' => 'string|min:3|max:255', + 'href' => 'required|string', + 'src' => 'required|string', + 'category' => 'string', + 'display' => 'required|bool', + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Models/Question.php b/modules/nuc_entities_structural/app/Models/Question.php new file mode 100644 index 0000000..d471cc1 --- /dev/null +++ b/modules/nuc_entities_structural/app/Models/Question.php @@ -0,0 +1,147 @@ +id; + } + + public function getIndex(): int + { + return $this->index; + } + + public function getContent(): string + { + return $this->content; + } + + public function getAnswer(): string + { + return $this->answer; + } + + public function getCategory(): ?string + { + return $this->category; + } + + public function getOnSite(): bool + { + return $this->on_site; + } + + public function getDisplay(): bool + { + return $this->display; + } + + public function getCreatedAt(): string + { + return $this->created_at; + } + + public function getUpdatedAt(): string + { + return $this->updated_at; + } + + /** + * Scope methods + */ + public function scopeGetById(Builder $query, int $parameter): Builder + { + return $query->where('id', $parameter); + } + + public function scopeGetByIndex(Builder $query, int $parameter): Builder + { + return $query->where('index', $parameter); + } + + public function scopeGetByContent(Builder $query, string $parameter): Builder + { + return $query->where('content', $parameter); + } + + public function scopeGetByAnswer(Builder $query, string $parameter): Builder + { + return $query->where('answer', $parameter); + } + + public function scopeGetByCategory(Builder $query, string $parameter): Builder + { + return $query->where('category', $parameter); + } + + public function scopeGetByOnSite(Builder $query, bool $parameter): Builder + { + return $query->where('on_site', $parameter); + } + + public function scopeGetByDisplay(Builder $query, bool $parameter): Builder + { + return $query->where('display', $parameter); + } + + public function scopeGetByCreatedAt(Builder $query, string $parameter): Builder + { + return $query->whereDate('created_at', $parameter); + } + + public function scopeGetByUpdatedAt(Builder $query, string $parameter): Builder + { + return $query->whereDate('updated_at', $parameter); + } +} diff --git a/modules/nuc_entities_structural/app/Models/Technology.php b/modules/nuc_entities_structural/app/Models/Technology.php new file mode 100644 index 0000000..fc484da --- /dev/null +++ b/modules/nuc_entities_structural/app/Models/Technology.php @@ -0,0 +1,147 @@ +id; + } + + public function getLabel(): string + { + return $this->label; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function getHref(): string + { + return $this->href; + } + + public function getSrc(): string + { + return $this->src; + } + + public function getCategory(): ?string + { + return $this->category; + } + + public function getDisplay(): bool + { + return $this->display; + } + + public function getCreatedAt(): string + { + return $this->created_at; + } + + public function getUpdatedAt(): string + { + return $this->updated_at; + } + + /** + * Scope methods + */ + public function scopeGetById(Builder $query, int $parameter): Builder + { + return $query->where('id', $parameter); + } + + public function scopeGetByLabel(Builder $query, string $parameter): Builder + { + return $query->where('label', $parameter); + } + + public function scopeGetByDescription(Builder $query, string $parameter): Builder + { + return $query->where('description', $parameter); + } + + public function scopeGetByHref(Builder $query, string $parameter): Builder + { + return $query->where('href', $parameter); + } + + public function scopeGetBySrc(Builder $query, string $parameter): Builder + { + return $query->where('src', $parameter); + } + + public function scopeGetByCategory(Builder $query, string $parameter): Builder + { + return $query->where('category', $parameter); + } + + public function scopeGetByDisplay(Builder $query, bool $parameter): Builder + { + return $query->where('display', $parameter); + } + + public function scopeGetByCreatedAt(Builder $query, string $parameter): Builder + { + return $query->whereDate('created_at', $parameter); + } + + public function scopeGetByUpdatedAt(Builder $query, string $parameter): Builder + { + return $query->whereDate('updated_at', $parameter); + } +} diff --git a/modules/nuc_entities_structural/app/Resources/QuestionResource.php b/modules/nuc_entities_structural/app/Resources/QuestionResource.php new file mode 100644 index 0000000..5a22bef --- /dev/null +++ b/modules/nuc_entities_structural/app/Resources/QuestionResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->getId(), + 'index' => $this->getIndex(), + 'content' => $this->getContent(), + 'answer' => $this->getAnswer(), + 'category' => $this->getCategory(), + 'on_site' => $this->getOnSite(), + 'display' => $this->getDisplay(), + 'created_at' => $this->getCreatedAt(), + 'updated_at' => $this->getUpdatedAt(), + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Resources/TechnologyResource.php b/modules/nuc_entities_structural/app/Resources/TechnologyResource.php new file mode 100644 index 0000000..588e575 --- /dev/null +++ b/modules/nuc_entities_structural/app/Resources/TechnologyResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->getId(), + 'label' => $this->getLabel(), + 'description' => $this->getDescription(), + 'href' => $this->getHref(), + 'src' => $this->getSrc(), + 'category' => $this->getCategory(), + 'display' => $this->getDisplay(), + 'created_at' => $this->getCreatedAt(), + 'updated_at' => $this->getUpdatedAt(), + ]; + } +} diff --git a/modules/nuc_entities_structural/app/Services/QuestionService.php b/modules/nuc_entities_structural/app/Services/QuestionService.php new file mode 100644 index 0000000..f898760 --- /dev/null +++ b/modules/nuc_entities_structural/app/Services/QuestionService.php @@ -0,0 +1,197 @@ +defineRequestData($request); + $this->defineUserData(); + + $result = $this->model->all(); + + $this->logger->logIndex($this->causer->name, $this->entity, $this->isRefererStructural); + + return QuestionResource::collection($result); + } + + /** + * @param Request $request + * + * @return int + * + * @throws Exception + */ + public function countByCreatedLastWeek(Request $request): int + { + $this->defineRequestData($request); + $this->defineTimeData(); + $this->defineUserData(); + + $result = $this->model->whereDate('created_at', '>=', $this->lastWeek)->count(); + + $this->logger->logCountByCreatedLastWeek($this->causer->name, $this->entity, $this->isRefererStructural); + + return $result; + } + + /** + * @param string $category + * + * @return AnonymousResourceCollection + * + * @throws Exception + */ + public function getByCategory(string $category): AnonymousResourceCollection + { + $this->defineUserData(); + + $result = $this->model::getByCategory($category)->get(); + + $this->logger->logMessage($this->causer->name . ' fetched questions by category: ' . $category . '.'); + + return QuestionResource::collection($result); + } + + /** + * @param string $site + * + * @return AnonymousResourceCollection + * + * @throws Exception + */ + public function getSiteQuestions(string $site): AnonymousResourceCollection + { + $this->defineUserData(); + + $result = $this->model::getByCategory($site)->get(); + + $name = $this->causer ? $this->causer->name : 'Guest'; + + $this->logger->logMessage($name . ' fetched questions by site: ' . $site . '.'); + + return QuestionResource::collection($result); + } + + /** + * @param string $site + * @param string $lang + * + * @return AnonymousResourceCollection + * + * @throws Exception + */ + public function getSiteQuestionsByLang(string $site, string $lang): AnonymousResourceCollection + { + $this->defineUserData(); + + $result = $this->model::getByCategory($site)->where('lang', $lang)->get(); + + $name = $this->causer ? $this->causer->name : 'Guest'; + + $this->logger->logMessage($name . ' fetched questions by site: ' . $site . ', lang: ' . $lang . '.'); + + return QuestionResource::collection($result); + } + + /** + * @param int $id + * + * @return QuestionResource + * + * @throws Exception + */ + public function show($id): QuestionResource + { + $this->defineUserData(); + + $result = $this->model::findOrFail($id); + + $this->logger->log($this->causer->name, $result->getContent(), $this->entity, 'showed'); + + return new QuestionResource($result); + } + + /** + * @param array $data + * + * @return QuestionResource + * + * @throws Exception + */ + public function create(array $data): QuestionResource + { + $this->defineUserData(); + + $result = $this->model::create($data); + + $this->logger->log($this->causer->name, $result->getContent(), $this->entity, 'created'); + + return new QuestionResource($result); + } + + /** + * @param int $id + * @param array $data + * + * @return QuestionResource + * + * @throws Exception + */ + public function update($id, array $data): QuestionResource + { + $this->defineUserData(); + + $result = $this->model::findOrFail($id); + + $result->update($data); + + $this->logger->log($this->causer->name, $result->getContent(), $this->entity, 'updated'); + + return new QuestionResource($result->fresh()); + } + + /** + * @param int $id + * + * @return void + * + * @throws Exception + */ + public function delete($id): void + { + $this->defineUserData(); + + $model = $this->model::findOrFail($id); + + $model->delete(); + + $this->logger->log($this->causer->name, $model->getContent(), $this->entity, 'deleted'); + } +} diff --git a/modules/nuc_entities_structural/app/Services/TechnologyService.php b/modules/nuc_entities_structural/app/Services/TechnologyService.php new file mode 100644 index 0000000..59d2afc --- /dev/null +++ b/modules/nuc_entities_structural/app/Services/TechnologyService.php @@ -0,0 +1,176 @@ +defineRequestData($request); + $this->defineUserData(); + + $result = $this->model->all(); + + $this->logger->logIndex($this->causer->name, $this->entity, true); + + return TechnologyResource::collection($result); + } + + /** + * @param Request $request + * + * @return int + * + * @throws Exception + */ + public function countByCreatedLastWeek(Request $request): int + { + $this->defineRequestData($request); + $this->defineTimeData(); + $this->defineUserData(); + + $result = $this->model->whereDate('created_at', '>=', $this->lastWeek)->count(); + + $this->logger->logCountByCreatedLastWeek($this->causer->name, $this->entity, true); + + return $result; + } + + /** + * @param string $category + * + * @return AnonymousResourceCollection + * + * @throws Exception + */ + public function getByCategory(string $category): AnonymousResourceCollection + { + $this->defineUserData(); + + $result = $this->model::getByCategory($category)->get(); + + $this->logger->logMessage($this->causer->name . ' fetched technologies by category: ' . $category . '.'); + + return TechnologyResource::collection($result); + } + + /** + * @param string $site + * + * @return AnonymousResourceCollection + * + * @throws Exception + */ + public function getSiteTechnologies(string $site): AnonymousResourceCollection + { + $this->defineUserData(); + + $result = $this->model::getByCategory($site)->get(); + + $name = $this->causer ? $this->causer->name : 'Guest'; + + $this->logger->logMessage($name . ' fetched technologies by site: ' . $site . '.'); + + return TechnologyResource::collection($result); + } + + /** + * @param int $id + * + * @return TechnologyResource + * + * @throws Exception + */ + public function show($id): TechnologyResource + { + $this->defineUserData(); + + $result = $this->model::findOrFail($id); + + $this->logger->log($this->causer->name, $result->getLabel(), $this->entity, 'showed'); + + return new TechnologyResource($result); + } + + /** + * @param array $data + * + * @return TechnologyResource + * + * @throws Exception + */ + public function create(array $data): TechnologyResource + { + $this->defineUserData(); + + $result = $this->model::create($data); + + $this->logger->log($this->causer->name, $result->getLabel(), $this->entity, 'created'); + + return new TechnologyResource($result); + } + + /** + * @param int $id + * @param array $data + * + * @return TechnologyResource + * + * @throws Exception + */ + public function update($id, array $data): TechnologyResource + { + $this->defineUserData(); + + $result = $this->model::findOrFail($id); + + $result->update($data); + + $this->logger->log($this->causer->name, $result->getLabel(), $this->entity, 'updated'); + + return new TechnologyResource($result->fresh()); + } + + /** + * @param int $id + * + * @return void + * + * @throws Exception + */ + public function delete($id): void + { + $this->defineUserData(); + + $model = $this->model::findOrFail($id); + + $model->delete(); + + $this->logger->log($this->causer->name, $model->getLabel(), $this->entity, 'deleted'); + } +} diff --git a/modules/nuc_entities_structural/atomic/bosons/constants/fields/index.ts b/modules/nuc_entities_structural/atomic/bosons/constants/fields/index.ts new file mode 100644 index 0000000..7d6f9c9 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/constants/fields/index.ts @@ -0,0 +1,2 @@ +export * from './question' +export * from './technology' diff --git a/modules/nuc_entities_structural/atomic/bosons/constants/fields/question.ts b/modules/nuc_entities_structural/atomic/bosons/constants/fields/question.ts new file mode 100644 index 0000000..716c251 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/constants/fields/question.ts @@ -0,0 +1,39 @@ +import type { EntityFieldInterface, UseFieldsInterface } from 'nucleify' + +export function useQuestionFields(): UseFieldsInterface { + const fieldData: readonly [string, string, string][] = [ + ['index', 'field-index', 'input-text'], + ['content', 'field-content', 'input-text'], + ['answer', 'field-answer', 'textarea'], + ['category', 'field-category', 'input-text'], + ['display', 'field-display', 'select'], + ['updated_at', 'field-updated-at', ''], + ['created_at', 'field-created-at', ''], + ] as const + + const displayOptions: readonly boolean[] = [true, false] + + const createAndEditFields: readonly EntityFieldInterface[] = fieldData + .filter(([name]) => !['created_at', 'updated_at'].includes(name)) + .map(([name, label, type]): EntityFieldInterface => { + const props = + name === 'display' + ? { options: displayOptions, placeholder: 'field-display-question' } + : undefined + + return { name, label, type, props } + }) + + const showFields: readonly { label: string; key: string }[] = fieldData.map( + ([key, label]) => ({ + name: key, + key, + label, + }) + ) + + return { + createAndEditFields, + showFields, + } +} diff --git a/modules/nuc_entities_structural/atomic/bosons/constants/fields/technology.ts b/modules/nuc_entities_structural/atomic/bosons/constants/fields/technology.ts new file mode 100644 index 0000000..c440905 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/constants/fields/technology.ts @@ -0,0 +1,43 @@ +import type { EntityFieldInterface, UseFieldsInterface } from 'nucleify' + +export function useTechnologyFields(): UseFieldsInterface { + const fieldData: readonly [string, string, string][] = [ + ['href', 'field-href', 'input-text'], + ['src', 'field-src', 'input-text'], + ['label', 'field-label', 'input-text'], + ['description', 'field-description', 'textarea'], + ['category', 'field-category', 'input-text'], + ['display', 'field-display', 'select'], + ['updated_at', 'field-updated-at', ''], + ['created_at', 'field-created-at', ''], + ] as const + + const displayOptions: readonly boolean[] = [true, false] + + const createAndEditFields: readonly EntityFieldInterface[] = fieldData + .filter(([name]) => !['created_at', 'updated_at'].includes(name)) + .map(([name, label, type]): EntityFieldInterface => { + const props = + name === 'display' + ? { + options: displayOptions, + placeholder: 'field-display-technology', + } + : undefined + + return { name, label, type, props } + }) + + const showFields: readonly { label: string; key: string }[] = fieldData.map( + ([key, label]) => ({ + name: key, + key, + label, + }) + ) + + return { + createAndEditFields, + showFields, + } +} diff --git a/modules/nuc_entities_structural/atomic/bosons/constants/index.ts b/modules/nuc_entities_structural/atomic/bosons/constants/index.ts new file mode 100644 index 0000000..b29bfea --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/constants/index.ts @@ -0,0 +1 @@ +export * from './fields' diff --git a/modules/nuc_entities_structural/atomic/bosons/index.ts b/modules/nuc_entities_structural/atomic/bosons/index.ts new file mode 100644 index 0000000..6eec6bb --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/index.ts @@ -0,0 +1,3 @@ +export * from './constants' +export * from './types' +export * from './utils' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/api/Question/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/api/Question/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/api/Question/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/api/Question/interfaces.ts b/modules/nuc_entities_structural/atomic/bosons/types/api/Question/interfaces.ts new file mode 100644 index 0000000..6ee88ee --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/api/Question/interfaces.ts @@ -0,0 +1,34 @@ +import type { + DeleteEntityRequestType, + EditEntityRequestType, + EntityCountResultsType, + EntityResultsType, + GetAllEntitiesRequestType, + GetEntitiesByCategoryRequestType, + GetEntityRequestType, + GetSiteEntitiesRequestType, + LoadingRefType, + NucQuestionObjectInterface, + StoreEntityRequestType, +} from 'nucleify' + +export interface NucQuestionRequestsInterface { + results: EntityResultsType + resultsByCategory: EntityResultsType + resultsByLang: EntityResultsType + resultsBySite: EntityResultsType + createdLastWeek: EntityCountResultsType + loading: LoadingRefType + getAllQuestions: GetAllEntitiesRequestType + getQuestionsByCategory: GetEntitiesByCategoryRequestType + getSiteQuestions: GetSiteEntitiesRequestType + getSiteQuestionsByLang: ( + site: SiteType, + lang: string, + loading?: boolean + ) => Promise + getCountQuestionsByCreatedLastWeek: GetEntityRequestType + storeQuestion: StoreEntityRequestType + editQuestion: EditEntityRequestType + deleteQuestion: DeleteEntityRequestType +} diff --git a/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/interfaces.ts b/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/interfaces.ts new file mode 100644 index 0000000..e34c7ab --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/api/Technology/interfaces.ts @@ -0,0 +1,28 @@ +import type { + DeleteEntityRequestType, + EditEntityRequestType, + EntityCountResultsType, + EntityResultsType, + GetAllEntitiesRequestType, + GetEntitiesByCategoryRequestType, + GetEntityRequestType, + GetSiteEntitiesRequestType, + LoadingRefType, + NucTechnologyObjectInterface, + StoreEntityRequestType, +} from 'nucleify' + +export interface NucTechnologyRequestsInterface { + results: EntityResultsType + resultsByCategory: EntityResultsType + resultsBySite: EntityResultsType + createdLastWeek: EntityCountResultsType + loading: LoadingRefType + getAllTechnologies: GetAllEntitiesRequestType + getTechnologiesByCategory: GetEntitiesByCategoryRequestType + getSiteTechnologies: GetSiteEntitiesRequestType + getCountTechnologiesByCreatedLastWeek: GetEntityRequestType + storeTechnology: StoreEntityRequestType + editTechnology: EditEntityRequestType + deleteTechnology: DeleteEntityRequestType +} diff --git a/modules/nuc_entities_structural/atomic/bosons/types/api/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/api/index.ts new file mode 100644 index 0000000..bc656ce --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/api/index.ts @@ -0,0 +1,2 @@ +export * from './Question' +export * from './Technology' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/index.ts new file mode 100644 index 0000000..d64bb2b --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/index.ts @@ -0,0 +1,2 @@ +export * from './api' +export * from './object' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/object/Question/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/object/Question/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/object/Question/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/object/Question/interfaces.ts b/modules/nuc_entities_structural/atomic/bosons/types/object/Question/interfaces.ts new file mode 100644 index 0000000..51e42ab --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/object/Question/interfaces.ts @@ -0,0 +1,11 @@ +export interface NucQuestionObjectInterface { + id?: number + index: number + content: string + answer: string + category: string + on_site?: boolean + display?: boolean + created_at?: string + updated_at?: string +} diff --git a/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/interfaces.ts b/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/interfaces.ts new file mode 100644 index 0000000..d83b044 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/object/Technology/interfaces.ts @@ -0,0 +1,12 @@ +export interface NucTechnologyObjectInterface { + id?: number + href?: string + src?: string + label?: string + description?: string + category?: string + display?: boolean + created_at?: string + updated_at?: string + prefix?: string +} diff --git a/modules/nuc_entities_structural/atomic/bosons/types/object/index.ts b/modules/nuc_entities_structural/atomic/bosons/types/object/index.ts new file mode 100644 index 0000000..bc656ce --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/types/object/index.ts @@ -0,0 +1,2 @@ +export * from './Question' +export * from './Technology' diff --git a/modules/nuc_entities_structural/atomic/bosons/utils/api/index.ts b/modules/nuc_entities_structural/atomic/bosons/utils/api/index.ts new file mode 100644 index 0000000..a456304 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/utils/api/index.ts @@ -0,0 +1,2 @@ +export * from './question_requests' +export * from './technology_requests' diff --git a/modules/nuc_entities_structural/atomic/bosons/utils/api/question_requests.ts b/modules/nuc_entities_structural/atomic/bosons/utils/api/question_requests.ts new file mode 100644 index 0000000..0a81e55 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/utils/api/question_requests.ts @@ -0,0 +1,155 @@ +'use client' + +import { useState } from 'react' + +import type { + CloseDialogType, + EntityCountResultsType, + EntityResultsType, + NucQuestionObjectInterface, + UseLoadingInterface, +} from 'nucleify' +import { apiHandle, useApiSuccess, useLoading } from 'nucleify' +import type { NucQuestionRequestsInterface } from '../../types/api' + +export function questionRequests( + close?: CloseDialogType +): NucQuestionRequestsInterface { + const [results, setResults] = useState< + EntityResultsType + >([]) + const [resultsByCategory, setResultsByCategory] = useState< + EntityResultsType + >([]) + const [resultsByLang, setResultsByLang] = useState< + EntityResultsType + >([]) + const [resultsBySite, setResultsBySite] = useState< + EntityResultsType + >([]) + const [createdLastWeek, setCreatedLastWeek] = + useState(0) + + const { loading, setLoading }: UseLoadingInterface = useLoading() + const { apiSuccess } = useApiSuccess() + + async function getAllQuestions(loading?: boolean): Promise { + await apiHandle({ + url: apiUrl() + '/questions', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucQuestionObjectInterface[]) => { + setResults(response) + }, + }) + } + + async function getCountQuestionsByCreatedLastWeek( + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + '/questions/count-by-created-last-week', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: number) => { + setCreatedLastWeek(response) + }, + }) + } + + async function getQuestionsByCategory( + category: string, + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + `/questions/get-by-category/${category}`, + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucQuestionObjectInterface[]) => { + setResultsByCategory(response) + apiSuccess(response) + }, + }) + } + + async function getSiteQuestions(site: string, loading?: boolean): Promise { + await apiHandle({ + url: apiUrl() + `/questions/get-site-questions/${site}`, + setLoading: loading ? setLoading : undefined, + onSuccess: (data: NucQuestionObjectInterface[]) => { + setResultsBySite(data) + }, + }) + } + + async function getSiteQuestionsByLang( + site: string, + lang: string, + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + `/questions/get-site-questions/${site}/${lang}`, + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucQuestionObjectInterface[]) => { + setResultsByLang(response) + }, + }) + } + + async function storeQuestion( + data: NucQuestionObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/questions', + method: 'POST', + data, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'create') + }, + }) + } + + async function editQuestion( + data: NucQuestionObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/questions', + method: 'PUT', + data, + id: data.id, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'edit') + }, + }) + } + + async function deleteQuestion( + id: number, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/questions', + method: 'DELETE', + id, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'delete') + }, + }) + } + + return { + results, + resultsByCategory, + resultsByLang, + resultsBySite, + createdLastWeek, + loading, + getAllQuestions, + getCountQuestionsByCreatedLastWeek, + getQuestionsByCategory, + getSiteQuestions, + getSiteQuestionsByLang, + storeQuestion, + editQuestion, + deleteQuestion, + } +} diff --git a/modules/nuc_entities_structural/atomic/bosons/utils/api/technology_requests.ts b/modules/nuc_entities_structural/atomic/bosons/utils/api/technology_requests.ts new file mode 100644 index 0000000..66ff29a --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/utils/api/technology_requests.ts @@ -0,0 +1,138 @@ +'use client' + +import { useState } from 'react' + +import type { + CloseDialogType, + EntityCountResultsType, + EntityResultsType, + NucTechnologyObjectInterface, + UseLoadingInterface, +} from 'nucleify' +import { apiHandle, useApiSuccess, useLoading } from 'nucleify' +import type { NucTechnologyRequestsInterface } from '../../types/api' + +export function technologyRequests( + close?: CloseDialogType +): NucTechnologyRequestsInterface { + const [results, setResults] = useState< + EntityResultsType + >([]) + const [resultsByCategory, setResultsByCategory] = useState< + EntityResultsType + >([]) + const [resultsBySite, setResultsBySite] = useState< + EntityResultsType + >([]) + const [createdLastWeek, setCreatedLastWeek] = + useState(0) + + const { loading, setLoading }: UseLoadingInterface = useLoading() + const { apiSuccess } = useApiSuccess() + + async function getAllTechnologies(loading?: boolean): Promise { + await apiHandle({ + url: apiUrl() + '/technologies', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucTechnologyObjectInterface[]) => { + setResults(response) + }, + }) + } + + async function getCountTechnologiesByCreatedLastWeek( + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + '/technologies/count-by-created-last-week', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: number) => { + setCreatedLastWeek(response) + }, + }) + } + + async function getTechnologiesByCategory( + category: string, + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + `/technologies/get-by-category/${category}`, + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucTechnologyObjectInterface[]) => { + setResultsByCategory(response) + }, + }) + } + + async function getSiteTechnologies( + site: string, + loading?: boolean + ): Promise { + await apiHandle({ + url: apiUrl() + `/technologies/get-site-technologies/${site}`, + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucTechnologyObjectInterface[]) => { + setResultsBySite(response) + }, + }) + } + + async function storeTechnology( + data: NucTechnologyObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/technologies', + method: 'POST', + data, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'create') + }, + }) + } + + async function editTechnology( + data: NucTechnologyObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/technologies', + method: 'PUT', + data, + id: data.id, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'edit') + }, + }) + } + + async function deleteTechnology( + id: number, + getData: () => Promise + ): Promise { + await apiHandle({ + url: apiUrl() + '/technologies', + method: 'DELETE', + id, + onSuccess: (response) => { + apiSuccess(response, getData, close, 'delete') + }, + }) + } + + return { + results, + resultsByCategory, + resultsBySite, + createdLastWeek, + loading, + getAllTechnologies, + getCountTechnologiesByCreatedLastWeek, + getTechnologiesByCategory, + getSiteTechnologies, + storeTechnology, + editTechnology, + deleteTechnology, + } +} diff --git a/modules/nuc_entities_structural/atomic/bosons/utils/index.ts b/modules/nuc_entities_structural/atomic/bosons/utils/index.ts new file mode 100644 index 0000000..3318fdb --- /dev/null +++ b/modules/nuc_entities_structural/atomic/bosons/utils/index.ts @@ -0,0 +1 @@ +export * from './api' diff --git a/modules/nuc_entities_structural/atomic/index.ts b/modules/nuc_entities_structural/atomic/index.ts new file mode 100644 index 0000000..677b9c2 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/index.ts @@ -0,0 +1,3 @@ +export * from './bosons' +export * from './pages' +export * from './templates' diff --git a/modules/nuc_entities_structural/atomic/pages/General/component.tsx b/modules/nuc_entities_structural/atomic/pages/General/component.tsx new file mode 100644 index 0000000..1fe5a90 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/General/component.tsx @@ -0,0 +1,120 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' + +import { useParams } from 'next/navigation' +import { useTranslation } from 'react-i18next' + +import { NucEntityChartCard } from '../../../../nuc_charts/atomic/template/entity-chart-card' +import { isMobile } from '../../../../nuc_media/utils/is_mobile' +import { NucTiles } from '../../../../nuc_templates/components/tiles' +import type { NucTilesInterface } from '../../../../nuc_templates/components/tiles/interfaces' +import { questionRequests, technologyRequests } from '../../bosons/utils' +import { NucQuestionDashboard, NucTechnologyDashboard } from '../../templates/Dashboard' + +export default function NucStructuralPage(): React.JSX.Element { + const params = useParams<{ lang?: string }>() + const lang = params?.lang || 'en' + const { t } = useTranslation() + + const { + results: questions, + createdLastWeek: questionsCreatedLastWeek, + loading: questionsLoading, + getAllQuestions, + getCountQuestionsByCreatedLastWeek, + } = questionRequests() + + const { + results: technologies, + createdLastWeek: technologiesCreatedLastWeek, + loading: technologiesLoading, + getAllTechnologies, + getCountTechnologiesByCreatedLastWeek, + } = technologyRequests() + + const [allLoaded, setAllLoaded] = useState(false) + + useEffect(() => { + void getAllQuestions(true) + void getCountQuestionsByCreatedLastWeek() + void getAllTechnologies(true) + void getCountTechnologiesByCreatedLastWeek() + }, []) + + useEffect(() => { + if (!questionsLoading && !technologiesLoading) { + const timeout = window.setTimeout(() => { + setAllLoaded(true) + }, 200) + + return () => { + window.clearTimeout(timeout) + } + } + + setAllLoaded(false) + return undefined + }, [questionsLoading, technologiesLoading]) + + const entities = useMemo( + () => [ + { + href: `/${lang}/structural/questions`, + header: t('admin-tile-questions'), + count: questions?.length || 0, + icon: 'prime:question', + countSecondary: questionsCreatedLastWeek, + textSecondary: t('admin-tile-this-week'), + adType: 'question', + }, + { + href: `/${lang}/structural/technologies`, + header: t('admin-tile-technologies'), + count: technologies?.length || 0, + icon: 'prime:microchip-ai', + countSecondary: technologiesCreatedLastWeek, + textSecondary: t('admin-tile-this-week'), + adType: 'technology', + }, + ], + [ + lang, + questions, + questionsCreatedLastWeek, + t, + technologies, + technologiesCreatedLastWeek, + ] + ) + + return ( +
+ + + + + + +
+ ) +} diff --git a/modules/nuc_entities_structural/atomic/pages/General/index.ts b/modules/nuc_entities_structural/atomic/pages/General/index.ts new file mode 100644 index 0000000..7623a22 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/General/index.ts @@ -0,0 +1 @@ +export { default as NucStructuralPage } from './component' diff --git a/modules/nuc_entities_structural/atomic/pages/Question/component.tsx b/modules/nuc_entities_structural/atomic/pages/Question/component.tsx new file mode 100644 index 0000000..a920983 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/Question/component.tsx @@ -0,0 +1,37 @@ +'use client' + +import { useEffect } from 'react' + +import { NucEntityChartCard } from '../../../../nuc_charts/atomic/template/entity-chart-card' +import { useNucDialog } from '../../../../nuc_dialog/utils/use_nuc_dialog' +import { isMobile } from '../../../../nuc_media/utils/is_mobile' +import { questionRequests } from '../../bosons/utils' +import { NucQuestionDashboard } from '../../templates/Dashboard' + +export default function NucQuestionPage(): React.JSX.Element { + const { closeDialog } = useNucDialog() + const { results, loading, getAllQuestions } = questionRequests(closeDialog) + + useEffect(() => { + void getAllQuestions(true) + }, []) + + return ( +
+ + +
+ ) +} diff --git a/modules/nuc_entities_structural/atomic/pages/Question/index.ts b/modules/nuc_entities_structural/atomic/pages/Question/index.ts new file mode 100644 index 0000000..5b6a084 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/Question/index.ts @@ -0,0 +1 @@ +export { default as NucQuestionPage } from './component' diff --git a/modules/nuc_entities_structural/atomic/pages/Technology/component.tsx b/modules/nuc_entities_structural/atomic/pages/Technology/component.tsx new file mode 100644 index 0000000..c72ea6a --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/Technology/component.tsx @@ -0,0 +1,37 @@ +'use client' + +import { useEffect } from 'react' + +import { NucEntityChartCard } from '../../../../nuc_charts/atomic/template/entity-chart-card' +import { useNucDialog } from '../../../../nuc_dialog/utils/use_nuc_dialog' +import { isMobile } from '../../../../nuc_media/utils/is_mobile' +import { technologyRequests } from '../../bosons/utils' +import { NucTechnologyDashboard } from '../../templates/Dashboard' + +export default function NucTechnologyPage(): React.JSX.Element { + const { closeDialog } = useNucDialog() + const { results, loading, getAllTechnologies } = technologyRequests(closeDialog) + + useEffect(() => { + void getAllTechnologies(true) + }, []) + + return ( +
+ + +
+ ) +} diff --git a/modules/nuc_entities_structural/atomic/pages/Technology/index.ts b/modules/nuc_entities_structural/atomic/pages/Technology/index.ts new file mode 100644 index 0000000..341a426 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/Technology/index.ts @@ -0,0 +1 @@ +export { default as NucTechnologyPage } from './component' diff --git a/modules/nuc_entities_structural/atomic/pages/index.ts b/modules/nuc_entities_structural/atomic/pages/index.ts new file mode 100644 index 0000000..0acd2e0 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/pages/index.ts @@ -0,0 +1,3 @@ +export * from './General' +export * from './Question' +export * from './Technology' diff --git a/modules/nuc_entities_structural/atomic/templates/Dashboard/Question.tsx b/modules/nuc_entities_structural/atomic/templates/Dashboard/Question.tsx new file mode 100644 index 0000000..e5b3b3c --- /dev/null +++ b/modules/nuc_entities_structural/atomic/templates/Dashboard/Question.tsx @@ -0,0 +1,164 @@ +'use client' + +import { useMemo } from 'react' + +import { useTranslation } from 'react-i18next' + +import { NucEntityDataTableCard } from '../../../../nuc_datatable/atomic/templates/entity-datatable-card' +import { NucDialog } from '../../../../nuc_dialog' +import type { ConfirmDialogFunctionType } from '../../../../nuc_dialog/types/functions' +import { useNucDialog } from '../../../../nuc_dialog/utils/use_nuc_dialog' +import type { NucDashboardInterface } from '../../../../nuc_templates/components/dashboard/types/interfaces' +import { useQuestionFields } from '../../bosons/constants/fields' +import type { NucQuestionObjectInterface } from '../../bosons/types' +import { questionRequests } from '../../bosons/utils' + +export default function NucQuestionDashboard( + props: NucDashboardInterface +): React.JSX.Element { + const { t } = useTranslation() + + const { + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + selectedObject, + openDialog, + closeDialog, + } = useNucDialog() + + const { createAndEditFields, showFields } = useQuestionFields() + const { deleteQuestion, storeQuestion, editQuestion } = + questionRequests(closeDialog) + + const confirmCreate: ConfirmDialogFunctionType = async (data, getData) => { + await storeQuestion(data as unknown as NucQuestionObjectInterface, async () => { + getData?.() + }) + } + + const confirmEdit: ConfirmDialogFunctionType = async (data, getData) => { + await editQuestion(data as unknown as NucQuestionObjectInterface, async () => { + getData?.() + }) + } + + const confirmDelete: ConfirmDialogFunctionType = async (id, getData) => { + if (typeof id !== 'number') return + await deleteQuestion(id, async () => { + getData?.() + }) + } + + const dialogs = useMemo( + () => [ + { + entity: 'question', + action: 'show', + visible: visibleShow, + data: selectedObject ? [selectedObject] : undefined, + cancelButtonLabel: t('common-close'), + fields: showFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + { + entity: 'question', + action: 'delete', + visible: visibleDelete, + title: t('entity-question-delete'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmDelete, + getData: props.getData, + }, + { + entity: 'question', + action: 'create', + visible: visibleCreate, + title: t('entity-question-create'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmCreate, + getData: props.getData, + fields: createAndEditFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + { + entity: 'question', + action: 'edit', + visible: visibleEdit, + data: selectedObject ? [selectedObject] : undefined, + title: t('entity-question-edit'), + confirmButtonLabel: t('common-update'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmEdit, + getData: props.getData, + fields: createAndEditFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + ], + [ + createAndEditFields, + confirmCreate, + confirmDelete, + confirmEdit, + props.getData, + selectedObject, + showFields, + t, + visibleCreate, + visibleDelete, + visibleEdit, + visibleShow, + ] + ) + + return ( +
+ [] | undefined} + loading={props.loading} + openDialog={openDialog} + tag={3} + adType="question" + headerText={t('entity-question-manage')} + buttonText={t('entity-question-new')} + /> + + {dialogs.map((dialog) => ( + closeDialog(dialog.action as ActionType)} + /> + ))} +
+ ) +} diff --git a/modules/nuc_entities_structural/atomic/templates/Dashboard/Technology.tsx b/modules/nuc_entities_structural/atomic/templates/Dashboard/Technology.tsx new file mode 100644 index 0000000..755e22a --- /dev/null +++ b/modules/nuc_entities_structural/atomic/templates/Dashboard/Technology.tsx @@ -0,0 +1,167 @@ +'use client' + +import { useMemo } from 'react' + +import { useTranslation } from 'react-i18next' + +import { NucEntityDataTableCard } from '../../../../nuc_datatable/atomic/templates/entity-datatable-card' +import { NucDialog } from '../../../../nuc_dialog' +import type { ConfirmDialogFunctionType } from '../../../../nuc_dialog/types/functions' +import { useNucDialog } from '../../../../nuc_dialog/utils/use_nuc_dialog' +import type { NucDashboardInterface } from '../../../../nuc_templates/components/dashboard/types/interfaces' +import { useTechnologyFields } from '../../bosons/constants/fields' +import type { NucTechnologyObjectInterface } from '../../bosons/types' +import { technologyRequests } from '../../bosons/utils' + +export default function NucTechnologyDashboard( + props: NucDashboardInterface +): React.JSX.Element { + const { t } = useTranslation() + + const { + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + selectedObject, + openDialog, + closeDialog, + } = useNucDialog() + + const { createAndEditFields, showFields } = useTechnologyFields() + const { deleteTechnology, storeTechnology, editTechnology } = + technologyRequests(closeDialog) + + const confirmCreate: ConfirmDialogFunctionType = async (data, getData) => { + await storeTechnology( + data as unknown as NucTechnologyObjectInterface, + async () => { + getData?.() + } + ) + } + + const confirmEdit: ConfirmDialogFunctionType = async (data, getData) => { + await editTechnology(data as unknown as NucTechnologyObjectInterface, async () => { + getData?.() + }) + } + + const confirmDelete: ConfirmDialogFunctionType = async (id, getData) => { + if (typeof id !== 'number') return + await deleteTechnology(id, async () => { + getData?.() + }) + } + + const dialogs = useMemo( + () => [ + { + entity: 'technology', + action: 'show', + visible: visibleShow, + data: selectedObject ? [selectedObject] : undefined, + cancelButtonLabel: t('common-close'), + fields: showFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + { + entity: 'technology', + action: 'delete', + visible: visibleDelete, + title: t('entity-technology-delete'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmDelete, + getData: props.getData, + }, + { + entity: 'technology', + action: 'create', + visible: visibleCreate, + title: t('entity-technology-create'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmCreate, + getData: props.getData, + fields: createAndEditFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + { + entity: 'technology', + action: 'edit', + visible: visibleEdit, + data: selectedObject ? [selectedObject] : undefined, + title: t('entity-technology-edit'), + confirmButtonLabel: t('common-update'), + cancelButtonLabel: t('common-cancel'), + confirm: confirmEdit, + getData: props.getData, + fields: createAndEditFields as unknown as Array<{ + name: string + label: string + type: string + key: string + props?: Record + }>, + }, + ], + [ + createAndEditFields, + confirmCreate, + confirmDelete, + confirmEdit, + props.getData, + selectedObject, + showFields, + t, + visibleCreate, + visibleDelete, + visibleEdit, + visibleShow, + ] + ) + + return ( +
+ [] | undefined} + loading={props.loading} + openDialog={openDialog} + tag={3} + adType="technology" + headerText={t('entity-technology-manage')} + buttonText={t('entity-technology-new')} + /> + + {dialogs.map((dialog) => ( + closeDialog(dialog.action as ActionType)} + /> + ))} +
+ ) +} diff --git a/modules/nuc_entities_structural/atomic/templates/Dashboard/index.ts b/modules/nuc_entities_structural/atomic/templates/Dashboard/index.ts new file mode 100644 index 0000000..b601dde --- /dev/null +++ b/modules/nuc_entities_structural/atomic/templates/Dashboard/index.ts @@ -0,0 +1,2 @@ +export { default as NucQuestionDashboard } from './Question' +export { default as NucTechnologyDashboard } from './Technology' diff --git a/modules/nuc_entities_structural/atomic/templates/index.ts b/modules/nuc_entities_structural/atomic/templates/index.ts new file mode 100644 index 0000000..f359847 --- /dev/null +++ b/modules/nuc_entities_structural/atomic/templates/index.ts @@ -0,0 +1 @@ +export * from './Dashboard' diff --git a/modules/nuc_entities_structural/config.json b/modules/nuc_entities_structural/config.json new file mode 100644 index 0000000..c8c0203 --- /dev/null +++ b/modules/nuc_entities_structural/config.json @@ -0,0 +1,8 @@ +{ + "name": "nuc_entities_structural", + "description": "Module that contains structural entity functions.", + "version": "0.3.4", + "category": "core", + "installed": true, + "enabled": true +} diff --git a/modules/nuc_entities_structural/database/constants/Questions/en/About.php b/modules/nuc_entities_structural/database/constants/Questions/en/About.php new file mode 100644 index 0000000..f61159c --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/en/About.php @@ -0,0 +1,28 @@ + 'Why was Nucleify created?', + 'answer' => 'Nucleify was designed to simplify page building and data management for both beginners and professionals.', + ], + [ + 'content' => 'What problems does it solve?', + 'answer' => 'It streamlines data migration, integration, and analysis while providing a flexible PageBuilder for creating dynamic web pages.', + ], + [ + 'content' => 'Who can benefit from using Nucleify?', + 'answer' => 'Nucleify is versatile and ideal for industries like finance, education, logistics, healthcare, and digital services.', + ], + [ + 'content' => 'Can I contribute to Nucleify?', + 'answer' => 'Yes! Developers, designers, and testers are welcome to contribute through GitHub, bug reports, or feature suggestions.', + ], + [ + 'content' => 'What license does it use?', + 'answer' => 'Nucleify is open-source under the BSD-3-Clause license, allowing free commercial and personal use.', + ], + [ + 'content' => 'Is it available in multiple languages?', + 'answer' => 'Yes, Nucleify plans to support multiple languages, making it accessible to users worldwide.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/en/Home.php b/modules/nuc_entities_structural/database/constants/Questions/en/Home.php new file mode 100644 index 0000000..c3d942b --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/en/Home.php @@ -0,0 +1,32 @@ + 'Will a website actually help me get more clients?', + 'answer' => 'Yes - a well-designed website works as your online storefront and a 24/7 salesperson. We focus not only on great design, but also on building trust and encouraging visitors to contact you. Our goal is simple: your website should generate real results, not just look good.', + ], + [ + 'content' => 'How much does a professional website really cost?', + 'answer' => 'The cost depends on your needs, but the good news is - you don’t need to spend a fortune to have a professional online presence. We offer flexible packages tailored to small businesses and individuals just getting started. Tell us what you need and we’ll provide a clear quote - with no hidden fees.', + ], + [ + 'content' => 'What are the monthly costs? Are there any hidden fees?', + 'answer' => 'We believe in full transparency. You’ll always know exactly what you’re paying for. No surprises, no hidden charges. Choose a plan that fits your budget, and we’ll explain everything clearly before we begin.', + ], + [ + 'content' => 'How quickly can my website go live?', + 'answer' => 'For a simple website, you could be online within just a few days. We work efficiently and keep you updated at every stage. The faster we receive your materials, the faster your website can start working for your business.', + ], + [ + 'content' => 'I’m not tech-savvy - will I be able to manage it?', + 'answer' => 'Absolutely. You don’t need any coding or technical knowledge. We explain everything in simple terms and guide you through the process. If you prefer, we can also handle everything for you so you don’t have to worry about a thing.', + ], + [ + 'content' => 'What if I want to make changes in the future?', + 'answer' => 'Your website can grow along with your business. You can add new services, pages, or features at any time. We remain flexible and continue supporting you even after your site goes live.', + ], + [ + 'content' => 'Why should I choose you?', + 'answer' => 'Because we focus on results. We build websites that create trust, attract clients, and help you sell. We care about design, performance, and security - but most importantly, we treat your business like it’s our own.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/en/Offer.php b/modules/nuc_entities_structural/database/constants/Questions/en/Offer.php new file mode 100644 index 0000000..e20da2c --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/en/Offer.php @@ -0,0 +1,40 @@ + 'How long does it take to build a website?', + 'answer' => 'Depending on the plan, delivery times range from 5 days for a simple one-page website to 2 weeks for a full MVP. We work efficiently without compromising quality.', + ], + [ + 'content' => 'Can I request changes after the website is delivered?', + 'answer' => 'Yes! All our plans include revision periods ranging from 7 to 30 days, ensuring you get exactly what you envisioned.', + ], + [ + 'content' => 'What payment options are available?', + 'answer' => 'We accept all major credit cards through Stripe. You can choose between monthly payments or a one-time payment with a 20% discount.', + ], + [ + 'content' => 'Is there a money-back guarantee?', + 'answer' => 'Yes, we offer a 30-day money-back guarantee. If you\'re not satisfied with our work, we\'ll refund your payment - no questions asked.', + ], + [ + 'content' => 'Can I request a custom plan?', + 'answer' => 'Yes! If none of our standard plans fit your needs, we can create a custom plan tailored to your project. Just reach out to us via the contact form and we\'ll put together a personalized offer.', + ], + [ + 'content' => 'Can I upgrade or change my plan later?', + 'answer' => 'Absolutely! You can upgrade your plan at any time. We\'ll apply the difference in pricing and seamlessly transition your project to the new tier.', + ], + [ + 'content' => 'What\'s included in the monthly subscription?', + 'answer' => 'Monthly plans include the full website development, hosting setup, and ongoing support. Minimum commitment is 6 months, after which you can cancel anytime.', + ], + [ + 'content' => 'Will I fully own the website once it\'s completed?', + 'answer' => 'Yes, you\'ll have full ownership of your website, including all source code, assets, and content. If you choose a monthly plan, ownership is transferred after the 6-month minimum commitment is completed. Once transferred, it\'s yours to keep forever.', + ], + [ + 'content' => 'Do you provide SEO and marketing support?', + 'answer' => 'All our plans include SEO basics. Higher-tier plans come with Google Analytics integration and advanced SEO optimization to help you rank better.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/en/Services.php b/modules/nuc_entities_structural/database/constants/Questions/en/Services.php new file mode 100644 index 0000000..9b55293 --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/en/Services.php @@ -0,0 +1,24 @@ + 'How does Nucleify handle data storage?', + 'answer' => 'Nucleify offers flexible data storage for modular web apps, supporting databases, cloud storage, or hybrid setups, allowing you to store and manage data according to your project needs.', + ], + [ + 'content' => 'Can it help migrate data between systems?', + 'answer' => 'Yes, Nucleify provides tools for smooth data migration between databases or legacy systems, minimizing disruption while ensuring your modular web apps stay fully functional.', + ], + [ + 'content' => 'How does Nucleify integrate with other software?', + 'answer' => 'Nucleify includes APIs, plugins, and pre-built connectors to integrate seamlessly with external services and popular software systems, extending the functionality of your web applications.', + ], + [ + 'content' => 'What data analysis tools are available in Nucleify?', + 'answer' => 'Nucleify offers built-in tools to filter, analyze, and visualize data, enabling actionable insights, report generation, and trend tracking for your web apps.', + ], + [ + 'content' => 'Can I create dynamic web pages with Nucleify?', + 'answer' => "Absolutely! Nucleify's Page Builder lets you design and customize content-rich, dynamic pages easily, without advanced coding, for modular and responsive web applications.", + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/pl/About.php b/modules/nuc_entities_structural/database/constants/Questions/pl/About.php new file mode 100644 index 0000000..e0faa9f --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/pl/About.php @@ -0,0 +1,28 @@ + 'Dlaczego powstał Nucleify?', + 'answer' => 'Nucleify został stworzony, aby uprościć budowanie stron i zarządzanie danymi zarówno dla początkujących, jak i profesjonalistów.', + ], + [ + 'content' => 'Jakie problemy rozwiązuje?', + 'answer' => 'Usprawnia migrację, integrację i analizę danych, jednocześnie oferując elastyczny PageBuilder do tworzenia dynamicznych stron internetowych.', + ], + [ + 'content' => 'Kto może skorzystać z Nucleify?', + 'answer' => 'Nucleify jest wszechstronny i idealny dla branż takich jak finanse, edukacja, logistyka, opieka zdrowotna i usługi cyfrowe.', + ], + [ + 'content' => 'Czy mogę wnieść swój wkład w Nucleify?', + 'answer' => 'Tak! Programiści, projektanci i testerzy mogą współtworzyć projekt poprzez GitHub, zgłaszanie błędów lub propozycje nowych funkcji.', + ], + [ + 'content' => 'Jakiej licencji używa?', + 'answer' => 'Nucleify jest open-source na licencji BSD-3-Clause, umożliwiającej bezpłatne użytkowanie komercyjne i osobiste.', + ], + [ + 'content' => 'Czy jest dostępny w wielu językach?', + 'answer' => 'Tak, Nucleify planuje obsługę wielu języków, co czyni go dostępnym dla użytkowników na całym świecie.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/pl/Home.php b/modules/nuc_entities_structural/database/constants/Questions/pl/Home.php new file mode 100644 index 0000000..f2d1764 --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/pl/Home.php @@ -0,0 +1,32 @@ + 'Czy strona internetowa faktycznie pomoże mi zdobyć klientów?', + 'answer' => 'Tak - dobrze zaprojektowana strona to Twoja wizytówka i sprzedawca 24/7. Dbamy nie tylko o wygląd, ale też o to, żeby strona budziła zaufanie i zachęcała do kontaktu. Naszym celem jest to, żeby Twoja strona realnie zarabiała, a nie tylko “ładnie wyglądała”.', + ], + [ + 'content' => 'Ile naprawdę kosztuje profesjonalna strona internetowa?', + 'answer' => 'Cena zależy od zakresu, ale dobra wiadomość jest taka: nie musisz wydawać fortuny, żeby mieć profesjonalną stronę. Mamy elastyczne pakiety dopasowane do małych firm i osób zaczynających. Powiedz nam, czego potrzebujesz, a przygotujemy konkretną wycenę - bez ukrytych kosztów.', + ], + [ + 'content' => 'Ile zapłacę miesięcznie? Czy są jakieś ukryte opłaty?', + 'answer' => 'Stawiamy na pełną przejrzystość. Otrzymujesz jasną informację, ile płacisz i za co. Żadnych niespodzianek ani ukrytych kosztów. Możesz wybrać plan dopasowany do budżetu, a my wszystko dokładnie wyjaśnimy przed startem.', + ], + [ + 'content' => 'Jak szybko moja strona może zacząć działać?', + 'answer' => 'W przypadku prostych stron możesz być online nawet w kilka dni. Działamy sprawnie i na każdym etapie informujemy Cię o postępach. Im szybciej dostaniemy materiały, tym szybciej Twoja strona zacznie pracować na Twój biznes.', + ], + [ + 'content' => 'Nie znam się na technologii - czy dam sobie radę?', + 'answer' => 'Oczywiście. Nie musisz znać się na kodowaniu ani technicznych sprawach. Wszystko tłumaczymy prostym językiem i pomagamy na każdym etapie. Możesz też zlecić nam pełną obsługę, jeśli nie chcesz zajmować się stroną samodzielnie.', + ], + [ + 'content' => 'Co jeśli będę chciał coś zmienić w przyszłości?', + 'answer' => 'Twoja strona może rozwijać się razem z Twoją firmą. W każdej chwili możesz dodać nowe usługi, podstrony czy funkcje. Jesteśmy elastyczni i wspieramy Cię także po uruchomieniu strony.', + ], + [ + 'content' => 'Dlaczego warto wybrać właśnie Was?', + 'answer' => 'Bo skupiamy się na efektach. Tworzymy strony, które budują zaufanie, przyciągają klientów i pomagają sprzedawać. Dbamy o estetykę, szybkość działania i bezpieczeństwo. A przede wszystkim - traktujemy Twój biznes tak, jakby był naszym własnym.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/pl/Offer.php b/modules/nuc_entities_structural/database/constants/Questions/pl/Offer.php new file mode 100644 index 0000000..cfbb860 --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/pl/Offer.php @@ -0,0 +1,40 @@ + 'Jak długo trwa budowa strony internetowej?', + 'answer' => 'W zależności od planu, czas realizacji wynosi od 5 dni dla prostej strony jednostronicowej do 2 tygodni dla pełnego MVP. Pracujemy efektywnie, nie rezygnując z jakości.', + ], + [ + 'content' => 'Czy mogę poprosić o zmiany po dostarczeniu strony?', + 'answer' => 'Tak! Wszystkie nasze plany obejmują okresy rewizji od 7 do 30 dni, zapewniając, że otrzymasz dokładnie to, co sobie wyobraziłeś.', + ], + [ + 'content' => 'Jakie opcje płatności są dostępne?', + 'answer' => 'Akceptujemy wszystkie główne karty kredytowe przez Stripe. Możesz wybrać między płatnościami miesięcznymi a jednorazową płatnością z 20% rabatem.', + ], + [ + 'content' => 'Czy jest gwarancja zwrotu pieniędzy?', + 'answer' => 'Tak, oferujemy 30-dniową gwarancję zwrotu pieniędzy. Jeśli nie będziesz zadowolony z naszej pracy, zwrócimy Ci płatność — bez zbędnych pytań.', + ], + [ + 'content' => 'Czy mogę zamówić indywidualny plan?', + 'answer' => 'Tak! Jeśli żaden z naszych standardowych planów nie odpowiada Twoim potrzebom, możemy stworzyć plan dopasowany do Twojego projektu. Skontaktuj się z nami przez formularz kontaktowy, a przygotujemy spersonalizowaną ofertę.', + ], + [ + 'content' => 'Czy mogę zmienić lub podwyższyć plan później?', + 'answer' => 'Oczywiście! Możesz podwyższyć swój plan w dowolnym momencie. Naliczymy różnicę w cenie i płynnie przeniesiemy Twój projekt na wyższy poziom.', + ], + [ + 'content' => 'Co obejmuje miesięczna subskrypcja?', + 'answer' => 'Plany miesięczne obejmują pełne tworzenie strony, konfigurację hostingu i bieżące wsparcie. Minimalny okres to 6 miesięcy, po którym możesz zrezygnować w dowolnym momencie.', + ], + [ + 'content' => 'Czy będę w pełni właścicielem strony po jej ukończeniu?', + 'answer' => 'Tak, będziesz mieć pełną własność swojej strony, w tym cały kod źródłowy, zasoby i treści. W przypadku planu miesięcznego, własność jest przenoszona po zakończeniu 6-miesięcznego minimalnego okresu. Po przeniesieniu jest Twoja na zawsze.', + ], + [ + 'content' => 'Czy zapewniacie wsparcie SEO i marketingowe?', + 'answer' => 'Wszystkie nasze plany zawierają podstawy SEO. Wyższe plany obejmują integrację z Google Analytics i zaawansowaną optymalizację SEO, aby pomóc Ci osiągnąć lepsze pozycje.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Questions/pl/Services.php b/modules/nuc_entities_structural/database/constants/Questions/pl/Services.php new file mode 100644 index 0000000..179963b --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Questions/pl/Services.php @@ -0,0 +1,24 @@ + 'Jak Nucleify obsługuje przechowywanie danych?', + 'answer' => 'Nucleify oferuje elastyczne przechowywanie danych dla modułowych aplikacji webowych, wspierając bazy danych, chmurę lub konfiguracje hybrydowe, pozwalając przechowywać i zarządzać danymi zgodnie z potrzebami projektu.', + ], + [ + 'content' => 'Czy pomaga w migracji danych między systemami?', + 'answer' => 'Tak, Nucleify zapewnia narzędzia do płynnej migracji danych między bazami danych lub starszymi systemami, minimalizując zakłócenia i utrzymując pełną funkcjonalność aplikacji.', + ], + [ + 'content' => 'Jak Nucleify integruje się z innym oprogramowaniem?', + 'answer' => 'Nucleify zawiera API, wtyczki i gotowe konektory do bezproblemowej integracji z zewnętrznymi usługami i popularnymi systemami, rozszerzając funkcjonalność Twoich aplikacji webowych.', + ], + [ + 'content' => 'Jakie narzędzia do analizy danych są dostępne?', + 'answer' => 'Nucleify oferuje wbudowane narzędzia do filtrowania, analizy i wizualizacji danych, umożliwiając generowanie raportów, śledzenie trendów i wyciąganie wniosków.', + ], + [ + 'content' => 'Czy mogę tworzyć dynamiczne strony w Nucleify?', + 'answer' => 'Oczywiście! Page Builder Nucleify pozwala projektować i dostosowywać bogate w treść, dynamiczne strony bez zaawansowanego kodowania, dla modułowych i responsywnych aplikacji webowych.', + ], +]; diff --git a/modules/nuc_entities_structural/database/constants/Technologies/General.php b/modules/nuc_entities_structural/database/constants/Technologies/General.php new file mode 100644 index 0000000..34757d7 --- /dev/null +++ b/modules/nuc_entities_structural/database/constants/Technologies/General.php @@ -0,0 +1,124 @@ + 'PHP', + 'description' => 'Popular server-side programming language, often used for creating dynamic websites and web applications.', + 'href' => 'https://www.php.net/', + 'src' => 'php.svg', + ], + [ + 'label' => 'Laravel', + 'description' => 'A PHP open-source framework designed for simple web application development with elegant and readable code.', + 'href' => 'https://laravel.com/', + 'src' => 'laravel.svg', + ], + [ + 'label' => 'TypeScript', + 'description' => 'A superset of JavaScript that adds static typing, which helps detect errors and improves code readability', + 'href' => 'https://www.typescriptlang.org/', + 'src' => 'typescript.svg', + ], + [ + 'label' => 'Nuxt', + 'description' => 'It provides a number of features that make it easy to build fast, SEO-friendly, and scalable web applications.', + 'href' => 'https://nuxt.com/', + 'src' => 'nuxt.svg', + ], + [ + 'label' => 'Vue', + 'description' => 'A progressive JavaScript framework for building user interfaces, known for its simplicity and performance.', + 'href' => 'https://vuejs.org/', + 'src' => 'vue.svg', + ], + [ + 'label' => 'PrimeVue', + 'description' => 'A UI component library for the Vue framework, offering a rich selection of ready-made user interface elements', + 'href' => 'https://primevue.org/', + 'src' => 'primevue.svg', + ], + [ + 'label' => 'GSAP', + 'description' => 'A JavaScript library for creating high-performance animations, easing, and transitions.', + 'href' => 'https://greensock.com/gsap/', + 'src' => 'gsap.svg', + ], + [ + 'label' => 'Chart.js', + 'description' => 'Simple yet flexible JavaScript charting library for the modern web', + 'href' => 'https://www.chartjs.org/', + 'src' => 'chart-js.svg', + ], + [ + 'label' => 'HTML5', + 'description' => 'The latest version of the HTML markup language. It enables the creation of responsive and semantic websites with multimedia support, without the need for additional plugins.', + 'href' => 'https://html.spec.whatwg.org/multipage/', + 'src' => 'html5.svg', + ], + [ + 'label' => 'SCSS (Sass)', + 'description' => 'A CSS preprocessor that extends its capabilities by adding variables, nesting rules, and other advanced features.', + 'href' => 'https://sass-lang.com/', + 'src' => 'scss.svg', + ], + [ + 'label' => 'MySQL', + 'description' => 'A relational database, popular in web applications. It enables the storage and management of large sets of data', + 'href' => 'https://www.mysql.com/', + 'src' => 'mysql.svg', + ], + [ + 'label' => 'Docker', + 'description' => 'A platform for containerizing applications, enabling the execution of apps in isolated environments. It simplifies the creation, deployment, and management of applications.', + 'href' => 'https://www.docker.com/', + 'src' => 'docker.svg', + ], + [ + 'label' => 'Heroku', + 'description' => 'A cloud-based PaaS (Platform-as-a-Service) platform, allowing easy deployment, scaling, and management of web applications', + 'href' => 'https://www.heroku.com/', + 'src' => 'heroku.svg', + ], + [ + 'label' => 'Vitest', + 'description' => 'A lightweight and fast testing framework for applications written in TypeScript and JavaScript, designed to work as a companion to Vite.', + 'href' => 'https://vitest.dev/', + 'src' => 'vitest.svg', + ], + [ + 'label' => 'Pest', + 'description' => 'A simple and elegant unit testing framework for PHP, designed with readability and performance in mind.', + 'href' => 'https://pestphp.com/', + 'src' => 'pest.svg', + ], + [ + 'label' => 'Cypress', + 'description' => 'An end-to-end testing framework for web applications. It allows fast and easy writing of user interface tests in real browsers.', + 'href' => 'https://www.cypress.io/', + 'src' => 'cypress.svg', + ], + [ + 'label' => 'Sonarcloud', + 'description' => 'A cloud-based tool for code quality analysis. It helps identify bugs, security issues, and technical debt.', + 'href' => 'https://www.sonarsource.com/products/sonarcloud/', + 'src' => 'sonarcloud.svg', + ], + [ + 'label' => 'Biome', + 'description' => 'A fast, modern, and powerful linter and formatter for JavaScript, TypeScript, and CSS.', + 'href' => 'https://biomejs.dev/', + 'src' => 'biome.svg', + ], + [ + 'label' => 'Stylelint', + 'description' => 'A linter for CSS and SCSS, helping enforce styling standards and detect errors in code.', + 'href' => 'https://stylelint.io/', + 'src' => 'stylelint.svg', + ], + [ + 'label' => 'Husky', + 'description' => 'A tool for managing Git hooks, enabling the execution of scripts (e.g., linting) before committing changes to a repository.', + 'href' => 'https://typicode.github.io/husky/', + 'src' => 'husky.svg', + ], +]; diff --git a/modules/nuc_entities_structural/database/factories/QuestionFactory.php b/modules/nuc_entities_structural/database/factories/QuestionFactory.php new file mode 100644 index 0000000..7ce82b4 --- /dev/null +++ b/modules/nuc_entities_structural/database/factories/QuestionFactory.php @@ -0,0 +1,44 @@ + + */ +class QuestionFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $category = $this->faker->randomElement(['home', 'about', 'offer', 'services', 'other']); + + $data = [ + 'index' => $this->faker->numberBetween(0, 100), + 'content' => $this->faker->sentence(), + 'answer' => $this->faker->sentence(10), + 'category' => $category, + 'on_site' => in_array($category, ['home', 'about', 'offer', 'services']), + 'display' => $this->faker->boolean(), + 'created_at' => $this->faker->dateTimeBetween('-1 year')->format('Y-m-d'), + 'updated_at' => $this->faker->dateTimeBetween('-1 year')->format('Y-m-d'), + ]; + + Validator::make($data, [ + 'index' => 'required|integer|min:0', + 'content' => 'required|string|max:255', + 'answer' => 'required|string|max:1000', + 'category' => 'string|max:255', + 'on_site' => 'bool', + 'display' => 'bool', + ]); + + return $data; + } +} diff --git a/modules/nuc_entities_structural/database/factories/TechnologyFactory.php b/modules/nuc_entities_structural/database/factories/TechnologyFactory.php new file mode 100644 index 0000000..5bfcd61 --- /dev/null +++ b/modules/nuc_entities_structural/database/factories/TechnologyFactory.php @@ -0,0 +1,42 @@ + + */ +class TechnologyFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $data = [ + 'label' => $this->faker->word(), + 'description' => $this->faker->sentence(5), + 'href' => $this->faker->url(), + 'src' => $this->faker->url(), + 'category' => $this->faker->word(), + 'display' => $this->faker->boolean(), + 'created_at' => $this->faker->dateTimeBetween('-1 year')->format('Y-m-d'), + 'updated_at' => $this->faker->dateTimeBetween('-1 year')->format('Y-m-d'), + ]; + + Validator::make($data, [ + 'label' => 'required|string|max:50', + 'description' => 'string|min:3|max:255', + 'href' => 'required|string', + 'src' => 'required|string', + 'category' => 'string', + 'display' => 'required|bool', + ]); + + return $data; + } +} diff --git a/modules/nuc_entities_structural/database/migrations/2025_01_13_135628_create_questions_table.php b/modules/nuc_entities_structural/database/migrations/2025_01_13_135628_create_questions_table.php new file mode 100644 index 0000000..e417984 --- /dev/null +++ b/modules/nuc_entities_structural/database/migrations/2025_01_13_135628_create_questions_table.php @@ -0,0 +1,34 @@ +id(); + $table->integer('index'); + $table->string('content'); + $table->text('answer'); + $table->string('category')->nullable(); + $table->string('lang', 2)->default('en'); + $table->boolean('on_site'); + $table->boolean('display'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('questions'); + } +}; diff --git a/modules/nuc_entities_structural/database/migrations/2025_02_22_235904_create_technologies_table.php b/modules/nuc_entities_structural/database/migrations/2025_02_22_235904_create_technologies_table.php new file mode 100644 index 0000000..a186b37 --- /dev/null +++ b/modules/nuc_entities_structural/database/migrations/2025_02_22_235904_create_technologies_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('label'); + $table->string('description')->nullable(); + $table->string('href'); + $table->string('src'); + $table->string('category')->nullable(); + $table->boolean('display'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('technologies'); + } +}; diff --git a/modules/nuc_entities_structural/database/seeders/EntitiesStructuralSeeder.php b/modules/nuc_entities_structural/database/seeders/EntitiesStructuralSeeder.php new file mode 100644 index 0000000..64dcd00 --- /dev/null +++ b/modules/nuc_entities_structural/database/seeders/EntitiesStructuralSeeder.php @@ -0,0 +1,16 @@ +call([ + QuestionSeeder::class, + TechnologySeeder::class, + ]); + } +} diff --git a/modules/nuc_entities_structural/database/seeders/QuestionSeeder.php b/modules/nuc_entities_structural/database/seeders/QuestionSeeder.php new file mode 100644 index 0000000..cdda5d0 --- /dev/null +++ b/modules/nuc_entities_structural/database/seeders/QuestionSeeder.php @@ -0,0 +1,34 @@ + 'About', 'home' => 'Home', 'offer' => 'Offer', 'services' => 'Services']; + + foreach ($languages as $lang) { + foreach ($categories as $category => $file) { + $questions = require $this->path . $lang . '/' . $file . '.php'; + + foreach ($questions as $question) { + Question::factory()->create(array_merge($question, [ + 'category' => $category, + 'lang' => $lang, + 'display' => true, + ])); + } + } + } + } +} diff --git a/modules/nuc_entities_structural/database/seeders/TechnologySeeder.php b/modules/nuc_entities_structural/database/seeders/TechnologySeeder.php new file mode 100644 index 0000000..9cf9a64 --- /dev/null +++ b/modules/nuc_entities_structural/database/seeders/TechnologySeeder.php @@ -0,0 +1,26 @@ +path . 'General.php'; + + foreach ($generalTechnologies as $technology) { + Technology::factory()->create(array_merge($technology, [ + 'category' => 'general', + 'display' => true, + ])); + } + } +} diff --git a/modules/nuc_entities_structural/index.ts b/modules/nuc_entities_structural/index.ts new file mode 100644 index 0000000..7ab945e --- /dev/null +++ b/modules/nuc_entities_structural/index.ts @@ -0,0 +1,14 @@ +/** + * Module's main file export + */ +export * from './nuc_entities_structural' + +/** + * Folders exports + */ +export * from './atomic' +export * from './vitests' + +/** + * File exports + */ diff --git a/modules/nuc_entities_structural/nuc_entities_structural.php b/modules/nuc_entities_structural/nuc_entities_structural.php new file mode 100644 index 0000000..c7e2bc0 --- /dev/null +++ b/modules/nuc_entities_structural/nuc_entities_structural.php @@ -0,0 +1,14 @@ +loadMigrationsFrom(__DIR__ . '/database/migrations'); + $this->loadRoutesFrom(__DIR__ . '/routes/api.php'); + } +} diff --git a/modules/nuc_entities_structural/nuc_entities_structural.ts b/modules/nuc_entities_structural/nuc_entities_structural.ts new file mode 100644 index 0000000..1e3abda --- /dev/null +++ b/modules/nuc_entities_structural/nuc_entities_structural.ts @@ -0,0 +1,20 @@ +import { + NucQuestionDashboard, + NucQuestionPage, + NucStructuralPage, + NucTechnologyDashboard, + NucTechnologyPage, +} from './atomic' + +interface ComponentRegistrar { + component: (name: string, component: unknown) => ComponentRegistrar +} + +export function registerNucEntitiesStructural(app: ComponentRegistrar): void { + app + .component('nuc-question-dashboard', NucQuestionDashboard) + .component('nuc-question-page', NucQuestionPage) + .component('nuc-structural-page', NucStructuralPage) + .component('nuc-technology-page', NucTechnologyPage) + .component('nuc-technology-dashboard', NucTechnologyDashboard) +} diff --git a/modules/nuc_entities_structural/routes/api.php b/modules/nuc_entities_structural/routes/api.php new file mode 100644 index 0000000..d6e448f --- /dev/null +++ b/modules/nuc_entities_structural/routes/api.php @@ -0,0 +1,60 @@ +group(function (): void { + Route::prefix('questions')->controller(QuestionController::class)->group(function (): void { + Route::get('/get-site-questions/{site}', 'getSiteQuestions') + ->name('questions.getSiteQuestions'); + + Route::get('/get-site-questions/{site}/{lang}', 'getSiteQuestionsByLang') + ->name('questions.getSiteQuestionsByLang'); + }); + + Route::get('/technologies/get-site-technologies/{site}', [TechnologyController::class, 'getSiteTechnologies']) + ->name('technologies.getSiteTechnologies'); + + Route::middleware(['web', 'auth'])->group(function (): void { + /** + * Questions + */ + Route::prefix('questions')->controller(QuestionController::class)->group(function (): void { + Route::get('/', 'index') + ->name('questions.index'); + Route::get('/count-by-created-last-week', 'countByCreatedLastWeek') + ->name('questions.countByCreatedLastWeek'); + Route::get('/get-by-category/{category}', 'getByCategory') + ->name('questions.getByCategory'); + Route::get('/{id}', 'show') + ->name('questions.show'); + Route::post('/', 'store') + ->name('questions.store'); + Route::put('/{id}', 'update') + ->name('questions.update'); + Route::delete('/{id}', 'destroy') + ->name('questions.destroy'); + }); + + /** + * Technologies + */ + Route::prefix('technologies')->controller(TechnologyController::class)->group(function (): void { + Route::get('/', 'index') + ->name('technologies.index'); + Route::get('/count-by-created-last-week', 'countByCreatedLastWeek') + ->name('technologies.countByCreatedLastWeek'); + Route::get('/get-by-category/{category}', 'getByCategory') + ->name('technologies.getByCategory'); + Route::get('/{id}', 'show') + ->name('technologies.show'); + Route::post('/', 'store') + ->name('technologies.store'); + Route::put('/{id}', 'update') + ->name('technologies.update'); + Route::delete('/{id}', 'destroy') + ->name('technologies.destroy'); + }); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Database/Factories/QuestionFactoryTest.php b/modules/nuc_entities_structural/tests/Database/Factories/QuestionFactoryTest.php new file mode 100644 index 0000000..eb79bf3 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Factories/QuestionFactoryTest.php @@ -0,0 +1,53 @@ +group('question-factory'); + +use App\Models\Question; + +beforeEach(function (): void { + $this->createUsers(); +}); + +test('can create record', function (): void { + $model = Question::factory()->create(); + + $this->assertDatabaseCount('questions', 1) + ->assertDatabaseHas('questions', ['id' => $model->id]); +}); + +test('can create multiple records', function (): void { + $models = Question::factory()->count(3)->create(); + + $this->assertDatabaseCount('questions', 3); + foreach ($models as $model) { + $this->assertDatabaseHas('questions', ['id' => $model->id]); + } +}); + +test('can\'t create record', function (): void { + try { + Question::factory()->create(['id' => 'id']); + } catch (Exception $e) { + $this->assertStringContainsString('Incorrect integer value', $e->getMessage()); + + return; + } + + $this->fail('Expected exception not thrown.'); +})->skip(env('DB_DATABASE') === 'database/database.sqlite', 'temporarily unavailable'); // unavailable for git workflow tests + +test('can\'t create multiple records', function (): void { + try { + Question::factory()->count(2)->create(['id' => 'id']); + } catch (Exception $e) { + $this->assertStringContainsString('Incorrect integer value', $e->getMessage()); + + return; + } + + $this->fail('Expected exception not thrown.'); +})->skip(env('DB_DATABASE') === 'database/database.sqlite', 'temporarily unavailable'); // unavailable for git workflow tests diff --git a/modules/nuc_entities_structural/tests/Database/Factories/TechnologyFactoryTest.php b/modules/nuc_entities_structural/tests/Database/Factories/TechnologyFactoryTest.php new file mode 100644 index 0000000..62569a9 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Factories/TechnologyFactoryTest.php @@ -0,0 +1,53 @@ +group('technology-factory'); + +use App\Models\Technology; + +beforeEach(function (): void { + $this->createUsers(); +}); + +test('can create record', function (): void { + $model = Technology::factory()->create(); + + $this->assertDatabaseCount('technologies', 1) + ->assertDatabaseHas('technologies', ['id' => $model->id]); +}); + +test('can create multiple records', function (): void { + $models = Technology::factory()->count(3)->create(); + + $this->assertDatabaseCount('technologies', 3); + foreach ($models as $model) { + $this->assertDatabaseHas('technologies', ['id' => $model->id]); + } +}); + +test('can\'t create record', function (): void { + try { + Technology::factory()->create(['id' => 'id']); + } catch (Exception $e) { + $this->assertStringContainsString('Incorrect integer value', $e->getMessage()); + + return; + } + + $this->fail('Expected exception not thrown.'); +})->skip(env('DB_DATABASE') === 'database/database.sqlite', 'temporarily unavailable'); // unavailable for git workflow tests + +test('can\'t create multiple records', function (): void { + try { + Technology::factory()->count(2)->create(['id' => 'id']); + } catch (Exception $e) { + $this->assertStringContainsString('Incorrect integer value', $e->getMessage()); + + return; + } + + $this->fail('Expected exception not thrown.'); +})->skip(env('DB_DATABASE') === 'database/database.sqlite', 'temporarily unavailable'); // unavailable for git workflow tests diff --git a/modules/nuc_entities_structural/tests/Database/Migrations/QuestionMigrationsTest.php b/modules/nuc_entities_structural/tests/Database/Migrations/QuestionMigrationsTest.php new file mode 100644 index 0000000..c32d33e --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Migrations/QuestionMigrationsTest.php @@ -0,0 +1,32 @@ +group('question-migrations'); + +use Illuminate\Support\Facades\Schema; + +test('can create table', function (): void { + expect(Schema::hasTable('questions')) + ->toBeTrue() + ->and(Schema::hasColumns('questions', [ + 'id', + 'index', + 'content', + 'answer', + 'category', + 'on_site', + 'display', + 'created_at', + 'updated_at', + ])) + ->toBeTrue(); +}); + +test('can be rolled back', function (): void { + $this->artisan('migrate:rollback'); + + expect(Schema::hasTable('questions'))->toBeFalse(); +}); diff --git a/modules/nuc_entities_structural/tests/Database/Migrations/TechnologyMigrationsTest.php b/modules/nuc_entities_structural/tests/Database/Migrations/TechnologyMigrationsTest.php new file mode 100644 index 0000000..f7ecd80 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Migrations/TechnologyMigrationsTest.php @@ -0,0 +1,32 @@ +group('technology-migrations'); + +use Illuminate\Support\Facades\Schema; + +test('can create table', function (): void { + expect(Schema::hasTable('technologies')) + ->toBeTrue() + ->and(Schema::hasColumns('technologies', [ + 'id', + 'label', + 'description', + 'href', + 'src', + 'category', + 'display', + 'created_at', + 'updated_at', + ])) + ->toBeTrue(); +}); + +test('can be rolled back', function (): void { + $this->artisan('migrate:rollback'); + + expect(Schema::hasTable('technologies'))->toBeFalse(); +}); diff --git a/modules/nuc_entities_structural/tests/Database/Models/QuestionTest.php b/modules/nuc_entities_structural/tests/Database/Models/QuestionTest.php new file mode 100644 index 0000000..004c3fa --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Models/QuestionTest.php @@ -0,0 +1,130 @@ +group('question-model'); + +use App\Models\Question; + +beforeEach(function (): void { + $this->createUsers(); + $this->model = Question::factory()->create(); +}); + +test('can be created', function (): void { + expect($this->model)->toBeInstanceOf(Question::class); +}); + +describe('Instance', function (): void { + test('can get id', function (): void { + expect($this->model->getId()) + ->toBeInt() + ->toBe($this->model->id); + }); + + test('can get index', function (): void { + expect($this->model->getIndex()) + ->toBeInt() + ->toBe($this->model->index); + }); + + test('can get content', function (): void { + expect($this->model->getContent()) + ->toBeString() + ->toBe($this->model->content); + }); + + test('can get answer', function (): void { + expect($this->model->getAnswer()) + ->toBeString() + ->toBe($this->model->answer); + }); + + test('can get category', function (): void { + expect($this->model->getCategory()) + ->toBeString() + ->toBe($this->model->category); + }); + + test('can get on_site', function (): void { + expect($this->model->getOnSite()) + ->toBeBool() + ->toBe($this->model->on_site); + }); + + test('can get display', function (): void { + expect($this->model->getDisplay()) + ->toBeBool() + ->toBe($this->model->display); + }); + + test('can get created_at date', function (): void { + expect($this->model->getCreatedAt()) + ->toBeString() + ->toBe($this->model->created_at->toDateTimeString()); + }); + + test('can get updated_at date', function (): void { + expect($this->model->getUpdatedAt()) + ->toBeString() + ->toBe($this->model->updated_at->toDateTimeString()); + }); +}); + +describe('Scope', function (): void { + test('can filter by id using scopeGetById', function (): void { + $foundModel = Question::getById($this->model->id)->first(); + + expect($foundModel->id)->toBe($this->model->id); + }); + + test('can filter by index using scopeGetByIndex', function (): void { + $foundModel = Question::getByIndex($this->model->index)->first(); + + expect($foundModel->index)->toBe($this->model->index); + }); + + test('can filter by content using scopeGetByContent', function (): void { + $foundModel = Question::getByContent($this->model->content)->first(); + + expect($foundModel->content)->toBe($this->model->content); + }); + + test('can filter by answer using scopeGetByAnswer', function (): void { + $foundModel = Question::getByAnswer($this->model->answer)->first(); + + expect($foundModel->answer)->toBe($this->model->answer); + }); + + test('can filter by category using scopeGetByCategory', function (): void { + $foundModel = Question::getByCategory($this->model->category)->first(); + + expect($foundModel->category)->toBe($this->model->category); + }); + + test('can filter by on_site using scopeGetByOnSite', function (): void { + $foundModel = Question::getByOnSite($this->model->on_site)->first(); + + expect($foundModel->on_site)->toEqual($this->model->on_site); + }); + + test('can filter by display using scopeGetByDisplay', function (): void { + $foundModel = Question::getByDisplay($this->model->display)->first(); + + expect($foundModel->display)->toEqual($this->model->display); + }); + + test('can filter by created_at using scopeGetByCreatedAt', function (): void { + $foundModel = Question::getByCreatedAt($this->model->created_at->toDateString())->first(); + + expect($foundModel->created_at->toDateString())->toBe($this->model->created_at->toDateString()); + }); + + test('can filter by updated_at using scopeGetByUpdatedAt', function (): void { + $foundModel = Question::getByUpdatedAt($this->model->updated_at->toDateString())->first(); + + expect($foundModel->updated_at->toDateString())->toBe($this->model->updated_at->toDateString()); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Database/Models/TechnologyTest.php b/modules/nuc_entities_structural/tests/Database/Models/TechnologyTest.php new file mode 100644 index 0000000..e6d2466 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Database/Models/TechnologyTest.php @@ -0,0 +1,130 @@ +group('technology-model'); + +use App\Models\Technology; + +beforeEach(function (): void { + $this->createUsers(); + $this->model = Technology::factory()->create(); +}); + +test('can be created', function (): void { + expect($this->model)->toBeInstanceOf(Technology::class); +}); + +describe('Instance', function (): void { + test('can get id', function (): void { + expect($this->model->getId()) + ->toBeInt() + ->toBe($this->model->id); + }); + + test('can get label', function (): void { + expect($this->model->getLabel()) + ->toBeString() + ->toBe($this->model->label); + }); + + test('can get description', function (): void { + expect($this->model->getDescription()) + ->toBeString() + ->toBe($this->model->description); + }); + + test('can get href', function (): void { + expect($this->model->getHref()) + ->toBeString() + ->toBe($this->model->href); + }); + + test('can get src', function (): void { + expect($this->model->getSrc()) + ->toBeString() + ->toBe($this->model->src); + }); + + test('can get category', function (): void { + expect($this->model->getCategory()) + ->toBeString() + ->toBe($this->model->category); + }); + + test('can get display', function (): void { + expect($this->model->getDisplay()) + ->toBeBool() + ->toBe($this->model->display); + }); + + test('can get created_at date', function (): void { + expect($this->model->getCreatedAt()) + ->toBeString() + ->toBe($this->model->created_at->toDateTimeString()); + }); + + test('can get updated_at date', function (): void { + expect($this->model->getUpdatedAt()) + ->toBeString() + ->toBe($this->model->updated_at->toDateTimeString()); + }); +}); + +describe('Scope', function (): void { + test('can filter by id using scopeGetById', function (): void { + $foundModel = Technology::getById($this->model->id)->first(); + + expect($foundModel->id)->toBe($this->model->id); + }); + + test('can filter by answer using scopeGetByLabel', function (): void { + $foundModel = Technology::getByLabel($this->model->label)->first(); + + expect($foundModel->label)->toBe($this->model->label); + }); + + test('can filter by category using scopeGetByDescription', function (): void { + $foundModel = Technology::getByDescription($this->model->description)->first(); + + expect($foundModel->description)->toBe($this->model->description); + }); + + test('can filter by id using scopeGetByHref', function (): void { + $foundModel = Technology::getByHref($this->model->href)->first(); + + expect($foundModel->href)->toBe($this->model->href); + }); + + test('can filter by id using scopeGetBySrc', function (): void { + $foundModel = Technology::getBySrc($this->model->src)->first(); + + expect($foundModel->src)->toBe($this->model->src); + }); + + test('can filter by category using scopeGetByCategory', function (): void { + $foundModel = Technology::getByCategory($this->model->category)->first(); + + expect($foundModel->category)->toBe($this->model->category); + }); + + test('can filter by display using scopeGetByDisplay', function (): void { + $foundModel = Technology::getByDisplay($this->model->display)->first(); + + expect($foundModel->display)->toEqual($this->model->display); + }); + + test('can filter by created_at using scopeGetByCreatedAt', function (): void { + $foundModel = Technology::getByCreatedAt($this->model->created_at->toDateString())->first(); + + expect($foundModel->created_at->toDateString())->toBe($this->model->created_at->toDateString()); + }); + + test('can filter by updated_at using scopeGetByUpdatedAt', function (): void { + $foundModel = Technology::getByUpdatedAt($this->model->updated_at->toDateString())->first(); + + expect($foundModel->updated_at->toDateString())->toBe($this->model->updated_at->toDateString()); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP200Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP200Test.php new file mode 100644 index 0000000..f6188da --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP200Test.php @@ -0,0 +1,72 @@ +group('question-api-200'); +uses()->group('api-200'); + +use App\Models\Question; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('200', function (): void { + test('index api', function (): void { + Question::factory(3)->create(); + + $this->getJson(route('questions.index')) + ->assertOk(); + }); + + test('countByCreatedLastWeek api', function (): void { + Question::factory(3)->create(); + + $this->getJson(route('questions.countByCreatedLastWeek')) + ->assertOk(); + }); + + test('getByCategory api', function (): void { + Question::factory(3)->create(['category' => 'technology']); + + $this->getJson(route('questions.getByCategory', ['category' => 'technology'])) + ->assertOk(); + }); + + test('getSiteQuestions api', function (): void { + Question::factory(3)->create(['category' => 'technology']); + + $this->getJson(route('questions.getSiteQuestions', ['site' => 'technology'])) + ->assertOk(); + }); + + test('store api', function (): void { + $this->postJson(route('questions.store'), questionData) + ->assertOk(); + }); + + test('show api', function (): void { + $model = Question::factory()->create(); + + $this->getJson(route('questions.show', $model->id)) + ->assertOk(); + }); + + test('update api', function (): void { + $model = Question::factory()->create(); + + $this->putJson(route('questions.update', $model->id), updatedQuestionData) + ->assertOk(); + }); + + test('destroy api', function (): void { + $model = Question::factory()->create(); + + $this->deleteJson(route('questions.destroy', $model->id)) + ->assertOk(); + $this->assertDatabaseMissing('questions', ['id' => $model->id]); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP302Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP302Test.php new file mode 100644 index 0000000..388196a --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP302Test.php @@ -0,0 +1,39 @@ +group('question-api-302'); +uses()->group('api-302'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('302', function (): void { + apiTestArray([ + 'put > show api' => [ + 'method' => 'PUT', + 'route' => 'questions.show', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + 'put > update api' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + 'put > delete api' => [ + 'method' => 'PUT', + 'route' => 'questions.destroy', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP401Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP401Test.php new file mode 100644 index 0000000..198f668 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP401Test.php @@ -0,0 +1,77 @@ +group('question-api-401'); +uses()->group('api-401'); + +describe('401', function (): void { + apiTestArray([ + 'index api' => [ + 'method' => 'GET', + 'route' => 'questions.index', + 'status' => 401, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'countByCreatedLastWeek api' => [ + 'method' => 'GET', + 'route' => 'questions.countByCreatedLastWeek', + 'status' => 401, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'show api' => [ + 'method' => 'SHOW', + 'route' => 'questions.show', + 'status' => 401, + 'id' => 1, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'store api with data' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'status' => 401, + 'data' => questionData, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'store api empty json' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'status' => 401, + 'data' => [], + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'update api with data' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'status' => 401, + 'id' => 1, + 'data' => questionData, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'update api empty json' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'status' => 401, + 'id' => 1, + 'data' => [], + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'destroy api' => [ + 'method' => 'DELETE', + 'route' => 'questions.destroy', + 'status' => 401, + 'id' => 1, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405AuthTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405AuthTest.php new file mode 100644 index 0000000..ce842da --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405AuthTest.php @@ -0,0 +1,96 @@ +group('question-api-405'); +uses()->group('question-api-405-auth'); +uses()->group('api-405'); +uses()->group('api-405-auth'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('405 > Authorized', function (): void { + apiTestArray([ + 'put > index api' => [ + 'method' => 'PUT', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'put json > index api' => [ + 'method' => 'PUT', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + ], + 'delete > index api' => [ + 'method' => 'DELETE', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'delete json > index api' => [ + 'method' => 'DELETE', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + ], + 'post json > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'questions.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + ], + 'post > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'questions.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > show api' => [ + 'method' => 'POST', + 'route' => 'questions.show', + 'status' => 405, + 'id' => 1, + ], + 'put json > post api' => [ + 'method' => 'PUT', + 'route' => 'questions.store', + 'status' => 405, + 'id' => 1, + ], + 'delete json > post api' => [ + 'method' => 'DELETE', + 'route' => 'questions.store', + 'status' => 405, + 'id' => 1, + ], + 'post json > update api' => [ + 'method' => 'POST', + 'route' => 'questions.update', + 'status' => 405, + 'id' => 1, + ], + 'post > delete api' => [ + 'method' => 'POST', + 'route' => 'questions.destroy', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > delete api' => [ + 'method' => 'POST', + 'route' => 'questions.destroy', + 'status' => 405, + 'id' => 1, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405UnAuthTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405UnAuthTest.php new file mode 100644 index 0000000..c1e60a4 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP405UnAuthTest.php @@ -0,0 +1,91 @@ +group('question-api-405'); +uses()->group('question-api-405-unauth'); +uses()->group('api-405'); +uses()->group('api-405-unauth'); + +describe('405 > Unauthorized', function (): void { + apiTestArray([ + 'put > index api' => [ + 'method' => 'PUT', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'put json > index api' => [ + 'method' => 'PUT', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + ], + 'delete > index api' => [ + 'method' => 'DELETE', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'delete json > index api' => [ + 'method' => 'DELETE', + 'route' => 'questions.index', + 'status' => 405, + 'id' => 1, + ], + 'post json > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'questions.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + ], + 'post > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'questions.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > show api' => [ + 'method' => 'POST', + 'route' => 'questions.show', + 'status' => 405, + 'id' => 1, + ], + 'put json > post api' => [ + 'method' => 'PUT', + 'route' => 'questions.store', + 'status' => 405, + 'id' => 1, + ], + 'delete json > post api' => [ + 'method' => 'DELETE', + 'route' => 'questions.store', + 'status' => 405, + 'id' => 1, + ], + 'post json > update api' => [ + 'method' => 'POST', + 'route' => 'questions.update', + 'status' => 405, + 'id' => 1, + ], + 'post > delete api' => [ + 'method' => 'POST', + 'route' => 'questions.destroy', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > delete api' => [ + 'method' => 'POST', + 'route' => 'questions.destroy', + 'status' => 405, + 'id' => 1, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PostTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PostTest.php new file mode 100644 index 0000000..8a9b69c --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PostTest.php @@ -0,0 +1,146 @@ +group('question-api-422'); +uses()->group('question-api-422-post'); +uses()->group('api-422'); +uses()->group('api-422-post'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('422 > POST', function (): void { + apiTestArray([ + // INDEX TESTS + 'index > empty' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['index' => '']), + 'structure' => ['errors' => ['index']], + 'fragment' => ['errors' => ['index' => ['The index field is required.']]], + ], + 'index > string' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['index' => 'index']), + 'structure' => ['errors' => ['index']], + 'fragment' => ['errors' => ['index' => ['The index field must be an integer.']]], + ], + 'index > false' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['index' => false]), + 'structure' => ['errors' => ['index']], + 'fragment' => ['errors' => ['index' => ['The index field must be an integer.']]], + ], + 'index > empty array' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['index' => []]), + 'structure' => ['errors' => ['index']], + 'fragment' => ['errors' => ['index' => ['The index field is required.']]], + ], + + // CONTENT TESTS + 'content > empty' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['content' => '']), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field is required.']]], + ], + 'content > integer' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['content' => 1]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > false' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['content' => false]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > true' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['content' => true]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > empty array' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['content' => []]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field is required.']]], + ], + + // ANSWER TESTS + 'answer > integer' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['answer' => 1]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > false' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['answer' => false]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > true' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['answer' => true]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > empty array' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['answer' => []]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field is required.']]], + ], + + // CATEGORY TESTS + 'category > integer' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['category' => 1]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > false' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['category' => false]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > true' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['category' => true]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > empty array' => [ + 'method' => 'POST', + 'route' => 'questions.store', + 'data' => array_merge(questionData, ['category' => []]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PutTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PutTest.php new file mode 100644 index 0000000..361a6a7 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP422PutTest.php @@ -0,0 +1,129 @@ +group('question-api-422'); +uses()->group('question-api-422-put'); +uses()->group('api-422'); +uses()->group('api-422-put'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('422 > PUT', function (): void { + apiTestArray([ + // CONTENT TESTS + 'content > empty' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['content' => '']), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field is required.']]], + ], + 'content > integer' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['content' => 1]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > false' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['content' => false]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > true' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['content' => true]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field must be a string.']]], + ], + 'content > empty array' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['content' => []]), + 'structure' => ['errors' => ['content']], + 'fragment' => ['errors' => ['content' => ['The content field is required.']]], + ], + + // ANSWER TESTS + 'answer > integer' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['answer' => 1]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > false' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['answer' => false]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > true' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['answer' => true]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field must be a string.']]], + ], + 'answer > empty array' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['answer' => []]), + 'structure' => ['errors' => ['answer']], + 'fragment' => ['errors' => ['answer' => ['The answer field is required.']]], + ], + + // CATEGORY TESTS + 'category > integer' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['category' => 1]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > false' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['category' => false]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > true' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['category' => true]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > empty array' => [ + 'method' => 'PUT', + 'route' => 'questions.update', + 'id' => 1, + 'data' => array_merge(updatedQuestionData, ['category' => []]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP500Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP500Test.php new file mode 100644 index 0000000..e8977a1 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Question/HTTP500Test.php @@ -0,0 +1,80 @@ +group('question-api-500'); +uses()->group('api-500'); + +use App\Models\Question; +use App\Services\QuestionService; + +use function Pest\Laravel\mock; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); + $this->service = mock(QuestionService::class); +}); + +describe('500', function (): void { + test('index api', function (): void { + $this->service + ->shouldReceive('index') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->getJson(route('questions.index')) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('show api', function (): void { + $this->service + ->shouldReceive('show') + ->with(1) + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->getJson(route('questions.show', ['id' => 1])) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('store api', function (): void { + $this->service + ->shouldReceive('create') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->postJson(route('questions.store'), questionData) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('update api', function (): void { + $this->service + ->shouldReceive('update') + ->with(1, Mockery::any()) + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->putJson(route('questions.update', questionData['id']), updatedQuestionData) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('destroy api', function (): void { + $model = Question::factory()->create(); + + $this->service + ->shouldReceive('delete') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->deleteJson(route('questions.destroy', ['id' => $model->id])) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP200Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP200Test.php new file mode 100644 index 0000000..99a827b --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP200Test.php @@ -0,0 +1,72 @@ +group('technology-api-200'); +uses()->group('api-200'); + +use App\Models\Technology; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('200', function (): void { + test('index api', function (): void { + Technology::factory(3)->create(); + + $this->getJson(route('technologies.index')) + ->assertOk(); + }); + + test('countByCreatedLastWeek api', function (): void { + Technology::factory(3)->create(); + + $this->getJson(route('technologies.countByCreatedLastWeek')) + ->assertOk(); + }); + + test('getByCategory api', function (): void { + Technology::factory(3)->create(['category' => 'technology']); + + $this->getJson(route('technologies.getByCategory', ['category' => 'technology'])) + ->assertOk(); + }); + + test('getSiteTechnologies api', function (): void { + Technology::factory(3)->create(['category' => 'technology']); + + $this->getJson(route('technologies.getSiteTechnologies', ['site' => 'technology'])) + ->assertOk(); + }); + + test('store api', function (): void { + $this->postJson(route('technologies.store'), technologyData) + ->assertOk(); + }); + + test('show api', function (): void { + $model = Technology::factory()->create(); + + $this->getJson(route('technologies.show', $model->id)) + ->assertOk(); + }); + + test('update api', function (): void { + $model = Technology::factory()->create(); + + $this->putJson(route('technologies.update', $model->id), updatedTechnologyData) + ->assertOk(); + }); + + test('destroy api', function (): void { + $model = Technology::factory()->create(); + + $this->deleteJson(route('technologies.destroy', $model->id)) + ->assertOk(); + $this->assertDatabaseMissing('technologies', ['id' => $model->id]); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP302Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP302Test.php new file mode 100644 index 0000000..42d3707 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP302Test.php @@ -0,0 +1,39 @@ +group('technology-api-302'); +uses()->group('api-302'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('302', function (): void { + apiTestArray([ + 'put > show api' => [ + 'method' => 'PUT', + 'route' => 'technologies.show', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + 'put > update api' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + 'put > delete api' => [ + 'method' => 'PUT', + 'route' => 'technologies.destroy', + 'status' => 302, + 'id' => 1, + 'json' => false, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP401Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP401Test.php new file mode 100644 index 0000000..7c5c28c --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP401Test.php @@ -0,0 +1,77 @@ +group('technology-api-401'); +uses()->group('api-401'); + +describe('401', function (): void { + apiTestArray([ + 'index api' => [ + 'method' => 'GET', + 'route' => 'technologies.index', + 'status' => 401, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'countByCreatedLastWeek api' => [ + 'method' => 'GET', + 'route' => 'technologies.countByCreatedLastWeek', + 'status' => 401, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'show api' => [ + 'method' => 'SHOW', + 'route' => 'technologies.show', + 'status' => 401, + 'id' => 1, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'store api with data' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'status' => 401, + 'data' => technologyData, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'store api empty json' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'status' => 401, + 'data' => [], + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'update api with data' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'status' => 401, + 'id' => 1, + 'data' => technologyData, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'update api empty json' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'status' => 401, + 'id' => 1, + 'data' => [], + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + 'destroy api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.destroy', + 'status' => 401, + 'id' => 1, + 'structure' => ['message'], + 'fragment' => ['message' => 'Unauthenticated.'], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405AuthTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405AuthTest.php new file mode 100644 index 0000000..048e2a9 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405AuthTest.php @@ -0,0 +1,96 @@ +group('technology-api-405'); +uses()->group('technology-api-405-auth'); +uses()->group('api-405'); +uses()->group('api-405-auth'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('405 > Authorized', function (): void { + apiTestArray([ + 'put > index api' => [ + 'method' => 'PUT', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'put json > index api' => [ + 'method' => 'PUT', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + ], + 'delete > index api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'delete json > index api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + ], + 'post json > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'technologies.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + ], + 'post > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'technologies.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > show api' => [ + 'method' => 'POST', + 'route' => 'technologies.show', + 'status' => 405, + 'id' => 1, + ], + 'put json > post api' => [ + 'method' => 'PUT', + 'route' => 'technologies.store', + 'status' => 405, + 'id' => 1, + ], + 'delete json > post api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.store', + 'status' => 405, + 'id' => 1, + ], + 'post json > update api' => [ + 'method' => 'POST', + 'route' => 'technologies.update', + 'status' => 405, + 'id' => 1, + ], + 'post > delete api' => [ + 'method' => 'POST', + 'route' => 'technologies.destroy', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > delete api' => [ + 'method' => 'POST', + 'route' => 'technologies.destroy', + 'status' => 405, + 'id' => 1, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405UnAuthTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405UnAuthTest.php new file mode 100644 index 0000000..0d85576 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP405UnAuthTest.php @@ -0,0 +1,91 @@ +group('technology-api-405'); +uses()->group('technology-api-405-unauth'); +uses()->group('api-405'); +uses()->group('api-405-unauth'); + +describe('405 > Unauthorized', function (): void { + apiTestArray([ + 'put > index api' => [ + 'method' => 'PUT', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'put json > index api' => [ + 'method' => 'PUT', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + ], + 'delete > index api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'delete json > index api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.index', + 'status' => 405, + 'id' => 1, + ], + 'post json > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'technologies.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + ], + 'post > countByCreatedLastWeek api' => [ + 'method' => 'POST', + 'route' => 'technologies.countByCreatedLastWeek', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > show api' => [ + 'method' => 'POST', + 'route' => 'technologies.show', + 'status' => 405, + 'id' => 1, + ], + 'put json > post api' => [ + 'method' => 'PUT', + 'route' => 'technologies.store', + 'status' => 405, + 'id' => 1, + ], + 'delete json > post api' => [ + 'method' => 'DELETE', + 'route' => 'technologies.store', + 'status' => 405, + 'id' => 1, + ], + 'post json > update api' => [ + 'method' => 'POST', + 'route' => 'technologies.update', + 'status' => 405, + 'id' => 1, + ], + 'post > delete api' => [ + 'method' => 'POST', + 'route' => 'technologies.destroy', + 'status' => 405, + 'id' => 1, + 'json' => false, + ], + 'post json > delete api' => [ + 'method' => 'POST', + 'route' => 'technologies.destroy', + 'status' => 405, + 'id' => 1, + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PostTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PostTest.php new file mode 100644 index 0000000..fe1109d --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PostTest.php @@ -0,0 +1,197 @@ +group('technology-api-422'); +uses()->group('technology-api-422-post'); +uses()->group('api-422'); +uses()->group('api-422-post'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('422 > POST', function (): void { + apiTestArray([ + // LABEL TESTS + 'label > empty' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['label' => '']), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field is required.']]], + ], + 'label > integer' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['label' => 1]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > false' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['label' => false]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > true' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['label' => true]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > empty array' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['label' => []]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field is required.']]], + ], + + // DESCRIPTION TESTS + 'description > integer' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['description' => 1]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > too short' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['description' => 't']), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be at least 3 characters.']]], + ], + 'description > false' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['description' => false]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > true' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['description' => true]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > empty array' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['description' => []]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + + // HREF TESTS + 'href > empty' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['href' => '']), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field is required.']]], + ], + 'href > integer' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['href' => 1]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > false' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['href' => false]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > true' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['href' => true]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > empty array' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['href' => []]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field is required.']]], + ], + + // SRC TESTS + 'src > empty' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['src' => '']), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field is required.']]], + ], + 'src > integer' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['src' => 1]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > false' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['src' => false]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > true' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['src' => true]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > empty array' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['src' => []]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field is required.']]], + ], + + // CATEGORY TESTS + 'category > integer' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['category' => 1]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > false' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['category' => false]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > true' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['category' => true]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > empty array' => [ + 'method' => 'POST', + 'route' => 'technologies.store', + 'data' => array_merge(technologyData, ['category' => []]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PutTest.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PutTest.php new file mode 100644 index 0000000..b4ec18f --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP422PutTest.php @@ -0,0 +1,221 @@ +group('technology-api-422'); +uses()->group('technology-api-422-put'); +uses()->group('api-422'); +uses()->group('api-422-put'); + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); +}); + +describe('422 > PUT', function (): void { + apiTestArray([ + // LABEL TESTS + 'label > empty' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['label' => '']), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field is required.']]], + ], + 'label > integer' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['label' => 1]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > false' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['label' => false]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > true' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['label' => true]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field must be a string.']]], + ], + 'label > empty array' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['label' => []]), + 'structure' => ['errors' => ['label']], + 'fragment' => ['errors' => ['label' => ['The label field is required.']]], + ], + + // DESCRIPTION TESTS + 'description > integer' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['description' => 1]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > too short' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['description' => 't']), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be at least 3 characters.']]], + ], + 'description > false' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['description' => false]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > true' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['description' => true]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + 'description > empty array' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['description' => []]), + 'structure' => ['errors' => ['description']], + 'fragment' => ['errors' => ['description' => ['The description field must be a string.', 'The description field must be at least 3 characters.']]], + ], + + // HREF TESTS + 'href > empty' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['href' => '']), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field is required.']]], + ], + 'href > integer' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['href' => 1]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > false' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['href' => false]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > true' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['href' => true]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field must be a string.']]], + ], + 'href > empty array' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['href' => []]), + 'structure' => ['errors' => ['href']], + 'fragment' => ['errors' => ['href' => ['The href field is required.']]], + ], + + // SRC TESTS + 'src > empty' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['src' => '']), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field is required.']]], + ], + 'src > integer' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['src' => 1]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > false' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['src' => false]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > true' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['src' => true]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field must be a string.']]], + ], + 'src > empty array' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['src' => []]), + 'structure' => ['errors' => ['src']], + 'fragment' => ['errors' => ['src' => ['The src field is required.']]], + ], + + // CATEGORY TESTS + 'category > integer' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['category' => 1]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > false' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['category' => false]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > true' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['category' => true]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + 'category > empty array' => [ + 'method' => 'PUT', + 'route' => 'technologies.update', + 'id' => 1, + 'data' => array_merge(updatedTechnologyData, ['category' => []]), + 'structure' => ['errors' => ['category']], + 'fragment' => ['errors' => ['category' => ['The category field must be a string.']]], + ], + ]); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP500Test.php b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP500Test.php new file mode 100644 index 0000000..b8b1be4 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Api/Technology/HTTP500Test.php @@ -0,0 +1,80 @@ +group('technology-api-500'); +uses()->group('api-500'); + +use App\Models\Technology; +use App\Services\TechnologyService; + +use function Pest\Laravel\mock; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); + $this->service = mock(TechnologyService::class); +}); + +describe('500', function (): void { + test('index api', function (): void { + $this->service + ->shouldReceive('index') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->getJson(route('technologies.index')) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('show api', function (): void { + $this->service + ->shouldReceive('show') + ->with(1) + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->getJson(route('technologies.show', ['id' => 1])) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('store api', function (): void { + $this->service + ->shouldReceive('create') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->postJson(route('technologies.store'), technologyData) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('update api', function (): void { + $this->service + ->shouldReceive('update') + ->with(1, Mockery::any()) + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->putJson(route('technologies.update', technologyData['id']), updatedTechnologyData) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); + + test('destroy api', function (): void { + $model = Technology::factory()->create(); + + $this->service + ->shouldReceive('delete') + ->once() + ->andThrow(new Exception('Internal Server Error')); + + $this->deleteJson(route('technologies.destroy', ['id' => $model->id])) + ->assertStatus(500) + ->assertJson(['error' => 'Internal Server Error']); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Controllers/QuestionControllerTest.php b/modules/nuc_entities_structural/tests/Feature/Controllers/QuestionControllerTest.php new file mode 100644 index 0000000..8acdb00 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Controllers/QuestionControllerTest.php @@ -0,0 +1,120 @@ +group('question-controller'); + +use App\Http\Controllers\QuestionController; +use App\Http\Requests\Question\PostRequest; +use App\Http\Requests\Question\PutRequest; +use App\Models\Question; +use App\Services\QuestionService; +use Illuminate\Http\Request; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); + $this->controller = app()->makeWith(QuestionController::class, ['questionService' => app()->make(QuestionService::class)]); +}); + +describe('200', function (): void { + test('index method', function (): void { + Question::factory()->count(3)->create(); + + $request = new Request; + + $response = $this->controller->index($request); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('countByCreatedLastWeek method', function (): void { + $request = new Request; + + $response = $this->controller->countByCreatedLastWeek($request); + + expect($response->getStatusCode())->toEqual(200); + }); + + test('getByCategory method', function (): void { + $category = 'technology'; + $categories = ['other', 'science', $category]; + + foreach ($categories as $cat) { + Question::factory()->create(['category' => $cat]); + } + + $response = $this->controller->getByCategory($category); + $data = $response->getData(true); + + expect($response->getStatusCode())->toEqual(200); + + foreach ($data as $model) { + expect($model['category'])->toEqual($category); + } + + expect(count($data))->toEqual(Question::where('category', $category)->count()); + }); + + test('getSiteQuestions method', function (): void { + $category = 'technology'; + $categories = ['other', 'science', $category]; + + foreach ($categories as $cat) { + Question::factory()->create(['category' => $cat]); + } + + $response = $this->controller->getSiteQuestions($category); + $data = $response->getData(true); + + expect($response->getStatusCode())->toEqual(200); + + foreach ($data as $model) { + expect($model['category'])->toEqual($category); + } + + expect(count($data))->toEqual(Question::where('category', $category)->count()); + }); + + test('show method', function (): void { + $model = Question::factory()->create(); + + $response = $this->controller->show($model->id); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('store method', function (): void { + $request = Mockery::mock(PostRequest::class); + $request->shouldReceive('validated') + ->andReturn(questionData); + + $response = $this->controller->store($request); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('update method', function (): void { + $model = Question::factory()->create(); + + $request = Mockery::mock(PutRequest::class); + $request->shouldReceive('validated') + ->andReturn(updatedQuestionData); + + $response = $this->controller->update($request, $model->id); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('delete method', function (): void { + $model = Question::factory()->create(); + + $response = $this->controller->destroy($model->id); + + expect($response->getStatusCode(), $response->getData(true)['deleted']) + ->toEqual(200) + ->and($this->assertDatabaseMissing('questions', ['id' => $model->id])); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Feature/Controllers/TechnologyControllerTest.php b/modules/nuc_entities_structural/tests/Feature/Controllers/TechnologyControllerTest.php new file mode 100644 index 0000000..3744758 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Feature/Controllers/TechnologyControllerTest.php @@ -0,0 +1,120 @@ +group('technology-controller'); + +use App\Http\Controllers\TechnologyController; +use App\Http\Requests\Technology\PostRequest; +use App\Http\Requests\Technology\PutRequest; +use App\Models\Technology; +use App\Services\TechnologyService; +use Illuminate\Http\Request; + +beforeEach(function (): void { + $this->createUsers(); + $this->actingAs($this->admin); + $this->controller = app()->makeWith(TechnologyController::class, ['technologyService' => app()->make(TechnologyService::class)]); +}); + +describe('200', function (): void { + test('index method', function (): void { + Technology::factory()->count(3)->create(); + + $request = new Request; + + $response = $this->controller->index($request); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('countByCreatedLastWeek method', function (): void { + $request = new Request; + + $response = $this->controller->countByCreatedLastWeek($request); + + expect($response->getStatusCode())->toEqual(200); + }); + + test('getByCategory method', function (): void { + $category = 'technology'; + $categories = ['other', 'science', $category]; + + foreach ($categories as $cat) { + Technology::factory()->create(['category' => $cat]); + } + + $response = $this->controller->getByCategory($category); + $data = $response->getData(true); + + expect($response->getStatusCode())->toEqual(200); + + foreach ($data as $model) { + expect($model['category'])->toEqual($category); + } + + expect(count($data))->toEqual(Technology::where('category', $category)->count()); + }); + + test('getSiteTechnologies method', function (): void { + $category = 'technology'; + $categories = ['other', 'science', $category]; + + foreach ($categories as $cat) { + Technology::factory()->create(['category' => $cat]); + } + + $response = $this->controller->getSiteTechnologies($category); + $data = $response->getData(true); + + expect($response->getStatusCode())->toEqual(200); + + foreach ($data as $model) { + expect($model['category'])->toEqual($category); + } + + expect(count($data))->toEqual(Technology::where('category', $category)->count()); + }); + + test('show method', function (): void { + $model = Technology::factory()->create(); + + $response = $this->controller->show($model->id); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('store method', function (): void { + $request = Mockery::mock(PostRequest::class); + $request->shouldReceive('validated') + ->andReturn(technologyData); + + $response = $this->controller->store($request); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('update method', function (): void { + $model = Technology::factory()->create(); + + $request = Mockery::mock(PutRequest::class); + $request->shouldReceive('validated') + ->andReturn(updatedTechnologyData); + + $response = $this->controller->update($request, $model->id); + + expect($response->getStatusCode(), $response->getData(true))->toEqual(200); + }); + + test('delete method', function (): void { + $model = Technology::factory()->create(); + + $response = $this->controller->destroy($model->id); + + expect($response->getStatusCode(), $response->getData(true)['deleted']) + ->toEqual(200) + ->and($this->assertDatabaseMissing('technologies', ['id' => $model->id])); + }); +}); diff --git a/modules/nuc_entities_structural/tests/Pest.php b/modules/nuc_entities_structural/tests/Pest.php new file mode 100644 index 0000000..d771411 --- /dev/null +++ b/modules/nuc_entities_structural/tests/Pest.php @@ -0,0 +1,13 @@ + 1, + 'user_id' => 1, + 'entity' => 'article', + 'value' => '#6d7c75', + 'new' => true, +]; + +const updatedColorData = [ + 'id' => 1, + 'user_id' => 1, + 'entity' => 'contact', + 'value' => '#39965b', + 'new' => false, +]; + +/** + * Question + */ +const questionData = [ + 'id' => 1, + 'index' => 1, + 'content' => 'Question', + 'answer' => 'Answer', + 'category' => 'test', + 'on_site' => true, + 'display' => true, +]; +const updatedQuestionData = [ + 'id' => 1, + 'index' => 1, + 'content' => 'Question2', + 'answer' => 'Answer2', + 'category' => 'test2', + 'on_site' => false, + 'display' => false, +]; + +/** + * Technology + */ +const technologyData = [ + 'id' => 1, + 'href' => 'href', + 'src' => 'src', + 'label' => 'Label', + 'description' => 'Description', + 'category' => 'test', + 'display' => true, +]; +const updatedTechnologyData = [ + 'id' => 1, + 'href' => 'href2', + 'src' => 'src2', + 'label' => 'Label2', + 'description' => 'Description2', + 'category' => 'test2', + 'display' => false, +]; diff --git a/modules/nuc_entities_structural/tests/TestGroups.php b/modules/nuc_entities_structural/tests/TestGroups.php new file mode 100644 index 0000000..a650349 --- /dev/null +++ b/modules/nuc_entities_structural/tests/TestGroups.php @@ -0,0 +1,82 @@ +group('nuc-entities-structural') + ->in('.'); + +uses() + ->group('nuc-entities-structural-db') + ->in('Database'); + +uses() + ->group('nuc-entities-structural-ft') + ->in('Feature'); + +/** + * Database groups + */ +uses() + ->group('database') + ->in('Database'); + +uses() + ->group('models') + ->in('Database/Models'); + +uses() + ->group('structural-model') + ->in('Database/Models'); + +uses() + ->group('migrations') + ->in('Database/Migrations'); + +uses() + ->group('structural-migrations') + ->in('Database/Migrations'); + +uses() + ->group('factories') + ->in('Database/Factories'); + +uses() + ->group('structural-factory') + ->in('Database/Factories'); + +/** + * Feature groups + */ +uses() + ->group('api') + ->in('Feature/Api'); + +uses() + ->group('question-api') + ->in('Feature/Api/Question'); + +uses() + ->group('technology-api') + ->in('Feature/Api/Technology'); + +uses() + ->group('feature') + ->in('Feature'); + +uses() + ->group('structural-feature') + ->in('Feature'); + +uses() + ->group('controllers') + ->in('Feature/Controllers'); + +uses() + ->group('structural-controller') + ->in('Feature/Controllers'); diff --git a/modules/nuc_entities_structural/tests/TestUses.php b/modules/nuc_entities_structural/tests/TestUses.php new file mode 100644 index 0000000..32c3831 --- /dev/null +++ b/modules/nuc_entities_structural/tests/TestUses.php @@ -0,0 +1,57 @@ +beforeEach(function (): void { + $this->artisan('migrate:fresh'); + }) + ->in('Feature', 'Database', 'Global'); +} else { + uses( + Tests\TestCase::class, + ) + ->in('Feature', 'Database'); + uses( + RefreshDatabase::class + ) + ->in( + // Question API + 'Feature/Api/Question/HTTP302Test.php', + + // Technology API + 'Feature/Api/Technology/HTTP302Test.php', + + 'Database/Models' + ); + + uses( + DatabaseMigrations::class + ) + ->in( + // Question API + 'Feature/Api/Question/HTTP200Test.php', + 'Feature/Api/Question/HTTP500Test.php', + 'Feature/Api/Question/HTTP422PostTest.php', + 'Feature/Api/Question/HTTP422PutTest.php', + + // Technology API + 'Feature/Api/Technology/HTTP200Test.php', + 'Feature/Api/Technology/HTTP500Test.php', + 'Feature/Api/Technology/HTTP422PostTest.php', + 'Feature/Api/Technology/HTTP422PutTest.php', + + 'Database/Factories', + 'Database/Migrations', + + 'Feature/Controllers', + 'Feature/Services', + 'Feature/Traits' + ); +} diff --git a/modules/nuc_entities_structural/vitests/api/Question/200.test.ts b/modules/nuc_entities_structural/vitests/api/Question/200.test.ts new file mode 100644 index 0000000..236c1cb --- /dev/null +++ b/modules/nuc_entities_structural/vitests/api/Question/200.test.ts @@ -0,0 +1,70 @@ +import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest' + +import * as nucleify from 'nucleify' + +describe('questionRequests', (): void => { + const { closeDialog } = nucleify.useNucDialog() + const requests: nucleify.NucQuestionRequestsInterface = + nucleify.questionRequests(closeDialog) + const mockResponse = [nucleify.mockQuestion] + + beforeEach((): void => { + vi.clearAllMocks() + nucleify.mockGlobalFetch(vi, mockResponse) + }) + + it('getAllQuestions', async (): Promise => { + await requests.getAllQuestions() + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('questions'), + expect.objectContaining({ method: 'GET' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('getCountQuestionsByCreatedLastWeek', async (): Promise => { + await requests.getCountQuestionsByCreatedLastWeek() + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('questions/count-by-created-last-week'), + expect.objectContaining({ method: 'GET' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('storeQuestion', async (): Promise => { + await requests.storeQuestion(nucleify.mockQuestion) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('questions'), + expect.objectContaining({ method: 'POST' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('editQuestion', async (): Promise => { + await requests.editQuestion(nucleify.mockQuestion) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('questions'), + expect.objectContaining({ method: 'PUT' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('deleteQuestion', async (): Promise => { + await requests.deleteQuestion(nucleify.mockQuestion.id ?? 0) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('questions'), + expect.objectContaining({ method: 'DELETE' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) +}) diff --git a/modules/nuc_entities_structural/vitests/api/Technology/200.test.ts b/modules/nuc_entities_structural/vitests/api/Technology/200.test.ts new file mode 100644 index 0000000..064c2cb --- /dev/null +++ b/modules/nuc_entities_structural/vitests/api/Technology/200.test.ts @@ -0,0 +1,59 @@ +import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest' + +import * as nucleify from 'nucleify' + +describe('technologyRequests', (): void => { + const { closeDialog } = nucleify.useNucDialog() + const requests: nucleify.NucTechnologyRequestsInterface = + nucleify.technologyRequests(closeDialog) + const mockResponse = [nucleify.mockTechnology] + + beforeEach((): void => { + vi.clearAllMocks() + nucleify.mockGlobalFetch(vi, mockResponse) + }) + + it('getAllTechnologies', async (): Promise => { + await requests.getAllTechnologies() + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('technologies'), + expect.objectContaining({ method: 'GET' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('storeTechnology', async (): Promise => { + await requests.storeTechnology(nucleify.mockTechnology) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('technologies'), + expect.objectContaining({ method: 'POST' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('editTechnology', async (): Promise => { + await requests.editTechnology(nucleify.mockTechnology) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('technologies'), + expect.objectContaining({ method: 'PUT' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) + + it('deleteTechnology', async (): Promise => { + await requests.deleteTechnology(nucleify.mockTechnology.id ?? 0) + expect( + (globalThis as unknown as { $fetch: Mock }).$fetch + ).toHaveBeenCalledWith( + expect.stringContaining('technologies'), + expect.objectContaining({ method: 'DELETE' }) + ) + expect(requests.results.value).toEqual(mockResponse) + }) +}) diff --git a/modules/nuc_entities_structural/vitests/constants/api/index.ts b/modules/nuc_entities_structural/vitests/constants/api/index.ts new file mode 100644 index 0000000..7d6f9c9 --- /dev/null +++ b/modules/nuc_entities_structural/vitests/constants/api/index.ts @@ -0,0 +1,2 @@ +export * from './question' +export * from './technology' diff --git a/modules/nuc_entities_structural/vitests/constants/api/question.ts b/modules/nuc_entities_structural/vitests/constants/api/question.ts new file mode 100644 index 0000000..fda58aa --- /dev/null +++ b/modules/nuc_entities_structural/vitests/constants/api/question.ts @@ -0,0 +1,11 @@ +import type { NucQuestionObjectInterface } from 'nucleify' + +export const mockQuestion: NucQuestionObjectInterface = { + id: 999, + index: Math.floor(Math.random() * 999), + content: 'Example question?', + answer: 'Example answer.', + category: 'example', + on_site: false, + display: false, +} diff --git a/modules/nuc_entities_structural/vitests/constants/api/technology.ts b/modules/nuc_entities_structural/vitests/constants/api/technology.ts new file mode 100644 index 0000000..2af3627 --- /dev/null +++ b/modules/nuc_entities_structural/vitests/constants/api/technology.ts @@ -0,0 +1,11 @@ +import type { NucTechnologyObjectInterface } from 'nucleify' + +export const mockTechnology: NucTechnologyObjectInterface = { + id: 999, + href: 'Example href', + src: 'Example src', + label: 'Example label', + description: 'Example description', + category: 'example', + display: false, +} diff --git a/modules/nuc_entities_structural/vitests/constants/index.ts b/modules/nuc_entities_structural/vitests/constants/index.ts new file mode 100644 index 0000000..3318fdb --- /dev/null +++ b/modules/nuc_entities_structural/vitests/constants/index.ts @@ -0,0 +1 @@ +export * from './api' diff --git a/modules/nuc_entities_structural/vitests/index.ts b/modules/nuc_entities_structural/vitests/index.ts new file mode 100644 index 0000000..f87cf01 --- /dev/null +++ b/modules/nuc_entities_structural/vitests/index.ts @@ -0,0 +1 @@ +export * from './constants'