
さらなる仕上げ
アンケート提出完了後の表示

英語になってるんのもあるけど、提出したのか提出してないのかわかり辛いのもあるから、完了日時でも出しときますか。
まず翻訳ストリング
まずShow Detailsは「詳細」くらいにしときますか?
"Show Details": "詳細",

完了日時
app/Http/Controllers/DashboardController.php
class DashboardController extends Controller
{
public function index(): Response
{
$surveys = Survey::latest()->get()->map(function ($survey) {
$response = $survey->responses()->where('user_id', auth()->id())->first();
$survey->response_id = $response ? $response->id : null;
$survey->response_created_at = $response ? $response->created_at : null; // 追加された行
return $survey;
});
return Inertia::render('Dashboard', [
'surveys' => $surveys
]);
}
}
で、response_created_at を渡している
でまあもうちょい拡張
resources/js/Pages/Dashboard.jsx
{survey.response_id ? (
<>
<PrimaryButton href={route('surveys.responses.show', {survey: survey.id, response: survey.response_id})}>
<VscEye className="mr-2" /> {t('Show Details')}
</PrimaryButton>
<p className="text-sm text-gray-700 mt-2 p-2 border border-blue-300 bg-blue-100 rounded">
{t("Submitted At")}:
{dayjs(survey.response_created_at).format('YYYY/MM/DD HH:mm:ss')}
<small className="ml-2">({dayjs(survey.response_created_at).fromNow()})</small>
</p>
</>
) : (
<PrimaryButton href={route('surveys.take', survey.id)}>
<VscChecklist className="mr-2" /> {t('Take The Survey')}
</PrimaryButton>
)}

デザインはまだチューニングのしようがあるかもしれない。とりあえずSecondaryButtonの方がいいかもなー

これに関して元々の仕様ではこう
一般ユーザーと管理者だけのシンプルな権限設定
survey自体の作成は管理者が行う
surveyが作成されたら各ユーザーは誰でも受けることができる
一度受けたsurveyは再度はできない。結果は即座に公開され自信の回答を見る事ができる。
何らかの集計viewを作成する
実は「何らかの集計view」の事も考えないといけないんだけど、それはまたちょっとこのコードがfreezeしてからでいいと思う。
breadcrumb
今、「詳細」を押すと出ていないので。

Breadcrumbs::for('surveys.take', function(BreadcrumbTrail $trail, Survey $survey)
{
$trail->parent('dashboard');
$trail->push($survey->title, route('surveys.take', $survey));
});
Breadcrumbs::for('surveys.responses.show', function(BreadcrumbTrail $trail, Survey $survv
ey, SurveyResponse $response)
{
$trail->parent('dashboard');
$trail->push($survey->title, route('surveys.responses.show', [$survey, $response]));
});

formリクエスト
今surveyの作成時においてバリデーションが極めて適当である。
public function store(Request $request, SurveyService $surveyService): RedirectResponse
{
$data = $request->all();
$data['settings'] = [];
// TODO: 本来はvalidationをかける
$surveyQuestionSetId = $request->survey_question_set_id;
$surveyStructure = SurveyQuestion::findOrFail($surveyQuestionSetId)->question_data;
DB::beginTransaction();
$survey = Survey::create($data);
$surveyService->createSurveyPage($survey, $surveyStructure);
DB::commit();
return redirect(route('surveys.index'))
->with(['success' => __('New Survey Created')])
;
}
これを何とかしていこう。てか何とかしないといけない所多すぎぃ

質問セットを選択してくださいとかその辺を一気にやっていくぞっと。
リクエスト側
% ./vendor/bin/sail artisan make:request SurveyRequest
INFO Request [app/Http/Requests/SurveyRequest.php] created successfully.
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'survey_question_set_id' => 'required|integer|exists:survey_questions,id',
];

これを適用してControllerを更新する
public function store(SurveyRequest $request, SurveyService $surveyService): RedirectResponse
{
$data = $request->validated();
$data['settings'] = [];
$surveyQuestionSetId = $request->survey_question_set_id;
$surveyStructure = SurveyQuestion::findOrFail($surveyQuestionSetId)->question_data;
DB::beginTransaction();
$survey = Survey::create($data);
$surveyService->createSurveyPage($survey, $surveyStructure);
DB::commit();
return redirect(route('surveys.index'))
->with(['success' => __('New Survey Created')])
;
}
フロントエンドも更新しておく。
resources/js/Pages/Surveys/Create.jsx
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, useForm } from '@inertiajs/react';
import { useLaravelReactI18n } from 'laravel-react-i18n';
import InputError from '@/Components/InputError';
import InputLabel from '@/Components/InputLabel';
import PrimaryButton from '@/Components/PrimaryButton';
import TextInput from '@/Components/TextInput';
export default function SurveyCreate({ auth, surveyQuestionSets }) {
const { t } = useLaravelReactI18n();
const {
data, setData, post, errors, processing,
} = useForm({
title: '',
description: '',
settings: '',
survey_question_set_id: '',
});
const submit = (e) => {
e.preventDefault();
post(route('surveys.store'));
};
return (
<AuthenticatedLayout
user={auth.user}
header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">{t('Create New Survey')}</h2>}
>
<Head title={t('Create New Survey')} />
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<form onSubmit={submit} className="mt-6 space-y-6">
<div>
<InputLabel htmlFor="title" value={t('Title')} />
<TextInput
id="title"
className="mt-1 block w-full"
value={data.title}
onChange={(e) => setData('title', e.target.value)}
onSubmit={submit}
required
/>
<InputError className="mt-2" message={errors.title} />
</div>
<div className="mt-3">
<InputLabel htmlFor="description" value={t('Description')} />
<TextInput
id="description"
className="mt-1 block w-full"
value={data.description}
onChange={(e) => setData('description', e.target.value)}
onSubmit={submit}
/>
<InputError className="mt-2" message={errors.description} />
</div>
<div className="mt-3">
<InputLabel htmlFor="survey_question_set_id" value={t('Question Set')} />
<select
id="survey_question_set_id"
className="mt-1 block w-full"
value={data.survey_question_set_id}
onChange={(e) => setData('survey_question_set_id', e.target.value)}
required
>
<option value="">
-- {t('Select a Question Set')} --
</option>
{Object.entries(surveyQuestionSets).map(([key, value]) => (
<option key={key} value={key}>
{value}
</option>
))}
</select>
<InputError className="mt-2" message={errors.survey_question_set_id} />
</div>
<div className="flex items-center gap-4">
<PrimaryButton disabled={processing}>{t('Save')}</PrimaryButton>
</div>
</form>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}
続いてEditも更新する
この場合validationがちょっと異なる。survey_question_set_idはcreateのときしか使わないからだ。この場合以下のようにしてもok
public function rules(): array
{
$rules = [
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'survey_question_set_id' => 'required|integer|exists:survey_questions,id',
'settings' => 'nullable|array',
];
if ($this->isMethod('patch') || $this->isMethod('put')) {
unset($rules['survey_question_set_id']);
}
return $rules;
}
settingsも付けておく。
でController
public function update(SurveyRequest $request, Survey $survey): RedirectResponse
{
$data = $request->validated();
$survey->update($data);
return redirect(route('surveys.index', $survey))
->with(['success' => __('Survey Settings Updated')]);
}
'Survey Settings Updated
これの言語も無かったので追加しておく
"Survey Settings Updated": "アンケート調査の設定を更新しました",