Skip to content

Commit

Permalink
IP-289: Create the new SolutionCreateEdit ViewModel
Browse files Browse the repository at this point in the history
  • Loading branch information
papandrk committed Nov 22, 2024
1 parent 9bfe46b commit ffe02b0
Show file tree
Hide file tree
Showing 11 changed files with 332 additions and 120 deletions.
2 changes: 1 addition & 1 deletion app/BusinessLogicLayer/Problem/ProblemManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function getCreateEditProblemViewModel(?int $id = null): CreateEditProble
} else {
$problem = new Problem;
$problem->default_language_id = 6; // @todo change with lookuptable value - bookmark2
$problem->setRelation('defaultTranslation', new ProblemTranslation); // bookmark2 - is this an "empty" relationship?
$problem->setRelation('defaultTranslation', new ProblemTranslation);
}

$translations = $this->problemTranslationManager->getTranslationsForProblem($problem);
Expand Down
88 changes: 87 additions & 1 deletion app/BusinessLogicLayer/Solution/SolutionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,92 @@

namespace App\BusinessLogicLayer\Solution;

use App\Models\Solution\Solution;
use App\Models\Solution\SolutionTranslation;
use App\Repository\LanguageRepository;
use App\Repository\Problem\ProblemRepository;
use App\Repository\Solution\SolutionRepository;
use App\Utils\FileHandler;
use App\ViewModels\Solution\CreateEditSolution;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

class SolutionManager {
public function __construct() {}
protected SolutionRepository $solutionRepository;
protected ProblemRepository $problemRepository;
protected SolutionTranslationManager $solutionTranslationManager;
protected SolutionStatusManager $solutionStatusManager;
protected LanguageRepository $languageRepository;

const DEFAULT_IMAGE_PATH = '/images/problem_default_image.png'; // bookmark3 - change this to the correct path

public function __construct(
SolutionRepository $solutionRepository,
ProblemRepository $problemRepository,
SolutionTranslationManager $solutionTranslationManager,
SolutionStatusManager $solutionStatusManager,
LanguageRepository $languageRepository
) {
$this->solutionRepository = $solutionRepository;
$this->problemRepository = $problemRepository;
$this->solutionTranslationManager = $solutionTranslationManager;
$this->solutionStatusManager = $solutionStatusManager;
$this->languageRepository = $languageRepository;
}

public function getCreateEditSolutionViewModel(int $problem_id, ?int $id = null): CreateEditSolution {
$problem = $this->problemRepository->find($problem_id);

if ($id) {
$solution = $this->solutionRepository->find($id);
} else {
$solution = new Solution;
$solution->setRelation('defaultTranslation', new SolutionTranslation);
}

$translations = $this->solutionTranslationManager->getTranslationsForSolution($solution);

$statusesLkp = $this->solutionStatusManager->getAllSolutionStatusesLkp();

$languagesLkp = $this->languageRepository->all();

return new CreateEditSolution(
$solution,
$translations,
$statusesLkp,
$languagesLkp,
$problem
);
}

public function storeSolution(array $attributes): int {
if (isset($attributes['solution-image']) && $attributes['solution-image']->isValid()) {
$imgPath = FileHandler::uploadAndGetPath($attributes['solution-image'], 'solution_img');
} else {
$imgPath = self::DEFAULT_IMAGE_PATH;
}


$solution_owner_problem_id = $attributes['solution-owner-problem'];

$default_language_id = $this->problemRepository->find($solution_owner_problem_id)->default_language_id;
$solution = Solution::create([
'problem_id' => $solution_owner_problem_id,
'user_creator_id' => Auth::id(),
'slug' => Str::random(16), // temporary - will be changed after record creation
'status_id' => $attributes['solution-status'],
'img_url' => $imgPath,
]);

$solution->slug = Str::slug($attributes['solution-title'] . '-' . $solution->id);
$solution->save();

$solution->defaultTranslation()->create([
'title' => $attributes['solution-title'],
'description' => $attributes['solution-description'],
'language_id' => $default_language_id,
]);

return $solution->id;
}
}
35 changes: 34 additions & 1 deletion app/BusinessLogicLayer/Solution/SolutionTranslationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@

namespace App\BusinessLogicLayer\Solution;

use App\Models\Solution\Solution;
use App\Repository\Solution\SolutionTranslationRepository;
use Illuminate\Support\Collection;

class SolutionTranslationManager {
public function __construct() {}
protected SolutionTranslationRepository $solutionTranslationRepository;

public function __construct(
SolutionTranslationRepository $solutionTranslationRepository,
) {
$this->solutionTranslationRepository = $solutionTranslationRepository;
}

/**
* Get all the translations for a solution.
*
* This function accepts either an integer ID or an instance of the
* Solution class and returns a Collection object
* with all of the solution's defined translations.
*
* @param int|Solution $input An integer ID or a Solution object.
*/
public function getTranslationsForSolution(int|Solution $input): Collection {
if (gettype($input) !== 'integer') {
$id = $input->id;
} else {
$id = $input;
}

if (!$id) {
return new Collection;
}

return $this->solutionTranslationRepository->allWhere(['solution_id' => $id]);
}
}
44 changes: 40 additions & 4 deletions app/Http/Controllers/Solution/SolutionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

namespace App\Http\Controllers\Solution;

use App\BusinessLogicLayer\Solution\SolutionManager;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class SolutionController extends Controller {
protected SolutionManager $solutionManager;

public function __construct(SolutionManager $solutionManager) {
$this->solutionManager = $solutionManager;
}

/**
* Display a listing of the resource.
*/
Expand All @@ -16,15 +25,42 @@ public function index() {
/**
* Show the form for creating a new resource.
*/
public function create() {
//
public function create(Request $request): View {
$this->validate($request, [
'problem_id' => ['required'],
]);
$viewModel = $this->solutionManager->getCreateEditSolutionViewModel($request->problem_id);

return view('backoffice.management.solution.create-edit.form-page', ['viewModel' => $viewModel]);
}

/**
* Store a newly created resource in storage.
*/
public function store(Request $request) {
//
public function store(Request $request): RedirectResponse {
$this->validate($request, [
'solution-title' => ['required', 'string', 'max:100'],
'solution-description' => ['required', 'string', 'max:400'],
'solution-status' => ['required'],
'solution-image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'solution-owner-problem' => ['required'],
]);

$attributes = $request->all();

try {
$createdSolutionId = $this->solutionManager->storeSolution($attributes);
} catch (\Exception $e) {
session()->flash('flash_message_error', 'Error: ' . $e->getCode() . ' ' . $e->getMessage());

return back()->withInput();
}

session()->flash('flash_message_success', 'Solution Created Successfully.');

$route = route('solutions.edit', ['solution' => $createdSolutionId]) . '?translations=1';

return redirect($route);
}

/**
Expand Down
41 changes: 40 additions & 1 deletion app/ViewModels/Solution/CreateEditSolution.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,43 @@

namespace App\ViewModels\Solution;

class CreateEditSolution {}
use App\Models\Problem\Problem;
use App\Models\Solution\Solution;
use Illuminate\Support\Collection;

class CreateEditSolution {
public Solution $solution;
public $translations;
public $solutionStatusesLkp;
public $languagesLkp;
public $problem;
public array $translationsMetaData;

public function __construct(
Solution $solution,
Collection $translations,
Collection $solutionStatusesLkp,
Collection $languagesLkp,
Problem $problem
) {
$this->solution = $solution;
$this->translations = $translations;
$this->solutionStatusesLkp = $solutionStatusesLkp;
$this->languagesLkp = $languagesLkp;
$this->problem = $problem;
$this->translationsMetaData = [
'title' => [
'display_title' => 'Solution title (*)',
'required' => true,
],
'description' => [
'display_title' => 'Solution description (*)',
'required' => true,
],
];
}

public function isEditMode(): bool {
return $this->solution->id !== null;
}
}
52 changes: 52 additions & 0 deletions resources/assets/js/solution/manage-solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { createApp } from "vue";
import store from "../store/store";

import TranslationsManager from "../vue-components/common/TranslationsManager.vue";

import $ from "jquery";

const app = createApp({
components: {
TranslationsManager,
},
});

app.use(store);
app.mount("#app");

(function () {

const initializeImgFileChangePreviewHandlers = function () {
$(".js-image-input").each(function (i, obj) {
$(obj).change(function () {
const event = this;
if (event.files && event.files[0]) {
const parent = $(obj).closest(".js-image-input-container");
const imgPreview = parent.find(".js-selected-image-preview");
const reader = new FileReader();
reader.onload = function (e) {
imgPreview.attr("src", e.target.result);
};
reader.readAsDataURL(event.files[0]);
}
});
});
};

const checkURLAndActivateTranslationsTab = function () {
// should check the URL for a `translations=1` variable and if set and if true, it should activate the tab.
if ( (window.location.search.indexOf("?translations=1") > -1) || (window.location.search.indexOf("&translations=1") > -1)) {
$("#translations-tab").click();
}
};

const init = function () {
initializeImgFileChangePreviewHandlers();
checkURLAndActivateTranslationsTab();
};

$(document).ready(function () {
init();
});

})();
16 changes: 16 additions & 0 deletions resources/assets/sass/solution/create-edit-solution.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import "../variables";
@import "../shared/image-input-preview.scss";

.explanation-text {
font-weight: normal;
color: $gray-600;
display: block;
margin-left: 20px;
line-height: 1.25;

ul {
list-style-type: none;
margin: 2px 2px 3px;
padding-left: 10px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@

@section('content-header')
<h1>{{ $viewModel->isEditMode() ? 'Edit' : 'Create' }}
Problem {{ $viewModel->isEditMode() ? ': ' . $viewModel->problem->defaultTranslation->name : '' }}
Solution {{ $viewModel->isEditMode() ? ': ' . $viewModel->solution->defaultTranslation->name : '' }}
<small class="font-weight-light">(required fields are marked with <span class="red">*</span>)</small></h1>
@endsection

@push('css')
@vite('resources/assets/sass/problem/create-edit-problem.scss')
@vite('resources/assets/sass/solution/create-edit-solution.scss')
@endpush

@section('content')

<form id="problem-form" enctype="multipart/form-data" method="POST"
action="{{ $viewModel->isEditMode() ? route('problems.update', $viewModel->problem) : route('problems.store') }}">
<form id="solution-form" enctype="multipart/form-data" method="POST"
action="{{ $viewModel->isEditMode() ? route('solutions.update', $viewModel->solution) : route('solutions.store') }}">

@if($viewModel->isEditMode())
@method('PUT')
@endif

@csrf

<input type="hidden" name="solution-owner-problem" value="{{ request()->problem_id}}">

<div class="container-fluid p-0">
<ul class="nav nav-tabs mt-4" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
Expand All @@ -35,11 +37,11 @@
<div class="tab-content " id="myTabContent">
<div class="tab-pane fade show active " id="basic-details" role="tabpanel"
aria-labelledby="basic-details-tab">
@include('backoffice.management.problem.create-edit.partials.basic-details')
@include('backoffice.management.solution.create-edit.partials.basic-details')

</div>
<div class="tab-pane fade " id="translations" role="tabpanel" aria-labelledby="translations-tab">
@include('backoffice.management.problem.create-edit.partials.translations')
@include('backoffice.management.solution.create-edit.partials.translations')
</div>
</div>
<div>
Expand All @@ -59,5 +61,5 @@
@endsection

@push('scripts')
@vite('resources/assets/js/problem/manage-problem.js')
@vite('resources/assets/js/solution/manage-solution.js')
@endpush
Loading

0 comments on commit ffe02b0

Please sign in to comment.