fixing bugs
This commit is contained in:
@@ -191,7 +191,7 @@ private function validateDetailsAnswer($question, $answer, array &$errors): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $hasRadioButtons && $details !== null && $details !== '') {
|
if (! $hasRadioButtons && $details !== null && $details !== '' && $details !== 'optional') {
|
||||||
if (! $answer || empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
|
if (! $answer || empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
|
||||||
$errors[] = "Question '{$question->text}' requires a text response.";
|
$errors[] = "Question '{$question->text}' requires a text response.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
final class AnswerResource extends Resource
|
final class AnswerResource extends Resource
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model the resource corresponds to.
|
* The model the resource corresponds to.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Nova\Dashboards;
|
namespace App\Nova\Dashboards;
|
||||||
|
|
||||||
use Laravel\Nova\Cards\Help;
|
use App\Nova\Metrics\ScreeningsTrend;
|
||||||
|
use App\Nova\Metrics\SessionsTrend;
|
||||||
|
use App\Nova\Metrics\TotalScreenings;
|
||||||
|
use App\Nova\Metrics\TotalSessions;
|
||||||
use Laravel\Nova\Dashboards\Main as Dashboard;
|
use Laravel\Nova\Dashboards\Main as Dashboard;
|
||||||
|
|
||||||
class Main extends Dashboard
|
final class Main extends Dashboard
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the cards for the dashboard.
|
* Get the cards for the dashboard.
|
||||||
@@ -15,7 +20,10 @@ class Main extends Dashboard
|
|||||||
public function cards(): array
|
public function cards(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new Help,
|
new TotalSessions,
|
||||||
|
new TotalScreenings,
|
||||||
|
new SessionsTrend,
|
||||||
|
new ScreeningsTrend,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
app/Nova/Metrics/ScreeningsTrend.php
Normal file
33
app/Nova/Metrics/ScreeningsTrend.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Nova\Metrics;
|
||||||
|
|
||||||
|
use App\Models\Screening;
|
||||||
|
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||||
|
use Laravel\Nova\Metrics\Trend;
|
||||||
|
|
||||||
|
final class ScreeningsTrend extends Trend
|
||||||
|
{
|
||||||
|
public ?int $cacheFor = null;
|
||||||
|
|
||||||
|
public function calculate(NovaRequest $request): mixed
|
||||||
|
{
|
||||||
|
return $this->countByDays($request, Screening::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name(): string
|
||||||
|
{
|
||||||
|
return 'Screenings';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ranges(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
30 => '30 Days',
|
||||||
|
60 => '60 Days',
|
||||||
|
90 => '90 Days',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Nova/Metrics/SessionsTrend.php
Normal file
33
app/Nova/Metrics/SessionsTrend.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Nova\Metrics;
|
||||||
|
|
||||||
|
use App\Models\Session;
|
||||||
|
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||||
|
use Laravel\Nova\Metrics\Trend;
|
||||||
|
|
||||||
|
final class SessionsTrend extends Trend
|
||||||
|
{
|
||||||
|
public function calculate(NovaRequest $request): mixed
|
||||||
|
{
|
||||||
|
return $this->countByDays($request, Session::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name(): string
|
||||||
|
{
|
||||||
|
return 'Sessions';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ranges(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
30 => '30 Days',
|
||||||
|
60 => '60 Days',
|
||||||
|
90 => '90 Days',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public $cacheFor = null;
|
||||||
|
}
|
||||||
47
app/Nova/Metrics/TotalScreenings.php
Normal file
47
app/Nova/Metrics/TotalScreenings.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Nova\Metrics;
|
||||||
|
|
||||||
|
use App\Models\Screening;
|
||||||
|
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||||
|
use Laravel\Nova\Metrics\Value;
|
||||||
|
|
||||||
|
final class TotalScreenings extends Value
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Calculate the value of the metric.
|
||||||
|
*/
|
||||||
|
public function calculate(NovaRequest $request): mixed
|
||||||
|
{
|
||||||
|
return $this->count($request, Screening::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the displayable name of the metric.
|
||||||
|
*/
|
||||||
|
public function name(): string
|
||||||
|
{
|
||||||
|
return 'Total Screenings';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ranges available for the metric.
|
||||||
|
*/
|
||||||
|
public function ranges(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
30 => '30 Days',
|
||||||
|
60 => '60 Days',
|
||||||
|
365 => '365 Days',
|
||||||
|
'TODAY' => 'Today',
|
||||||
|
'ALL' => 'All Time',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the amount of time the results should be cached.
|
||||||
|
*/
|
||||||
|
public $cacheFor = null;
|
||||||
|
}
|
||||||
35
app/Nova/Metrics/TotalSessions.php
Normal file
35
app/Nova/Metrics/TotalSessions.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Nova\Metrics;
|
||||||
|
|
||||||
|
use App\Models\Session;
|
||||||
|
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||||
|
use Laravel\Nova\Metrics\Value;
|
||||||
|
|
||||||
|
final class TotalSessions extends Value
|
||||||
|
{
|
||||||
|
public function calculate(NovaRequest $request): mixed
|
||||||
|
{
|
||||||
|
return $this->count($request, Session::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name(): string
|
||||||
|
{
|
||||||
|
return 'Total Sessions';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ranges(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
30 => '30 Days',
|
||||||
|
60 => '60 Days',
|
||||||
|
365 => '365 Days',
|
||||||
|
'TODAY' => 'Today',
|
||||||
|
'ALL' => 'All Time',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public $cacheFor = null;
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ final class QuestionGroupResource extends Resource
|
|||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public static $displayInNavigation = false;
|
public static $displayInNavigation = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the displayable label of the resource.
|
* Get the displayable label of the resource.
|
||||||
|
|||||||
@@ -9,6 +9,17 @@
|
|||||||
|
|
||||||
abstract class Resource extends NovaResource
|
abstract class Resource extends NovaResource
|
||||||
{
|
{
|
||||||
|
public static function perPageOptions()
|
||||||
|
{
|
||||||
|
return [50, 100, 150];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function perPageViaRelationshipOptions()
|
||||||
|
{
|
||||||
|
return [10, 25, 50];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an "index" query for the given resource.
|
* Build an "index" query for the given resource.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -108,8 +108,7 @@ public function fields(NovaRequest $request): array
|
|||||||
])
|
])
|
||||||
->displayUsingLabels()
|
->displayUsingLabels()
|
||||||
->sortable()
|
->sortable()
|
||||||
->filterable()
|
->filterable(),
|
||||||
->readonly(),
|
|
||||||
|
|
||||||
Number::make('Score')
|
Number::make('Score')
|
||||||
->sortable()
|
->sortable()
|
||||||
@@ -125,8 +124,7 @@ public function fields(NovaRequest $request): array
|
|||||||
])
|
])
|
||||||
->displayUsingLabels()
|
->displayUsingLabels()
|
||||||
->sortable()
|
->sortable()
|
||||||
->filterable()
|
->filterable(),
|
||||||
->readonly(),
|
|
||||||
|
|
||||||
Textarea::make('Additional Comments')
|
Textarea::make('Additional Comments')
|
||||||
->rules('nullable'),
|
->rules('nullable'),
|
||||||
|
|||||||
@@ -74,19 +74,16 @@ public function fields(NovaRequest $request): array
|
|||||||
Text::make('Job Title', 'job_title')
|
Text::make('Job Title', 'job_title')
|
||||||
->sortable()
|
->sortable()
|
||||||
->filterable()
|
->filterable()
|
||||||
->copyable()
|
->copyable(),
|
||||||
->readonly(),
|
|
||||||
|
|
||||||
Text::make('Department')
|
Text::make('Department')
|
||||||
->sortable()
|
->sortable()
|
||||||
->filterable()
|
->filterable()
|
||||||
->copyable()
|
->copyable(),
|
||||||
->readonly(),
|
|
||||||
|
|
||||||
Text::make('Phone')
|
Text::make('Phone')
|
||||||
->sortable()
|
->sortable()
|
||||||
->copyable()
|
->copyable(),
|
||||||
->readonly(),
|
|
||||||
|
|
||||||
Password::make('Password')
|
Password::make('Password')
|
||||||
->onlyOnForms()
|
->onlyOnForms()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
use App\Nova\CategoryResource;
|
use App\Nova\CategoryResource;
|
||||||
use App\Nova\Dashboards\Main;
|
use App\Nova\Dashboards\Main;
|
||||||
use App\Nova\LogResource;
|
use App\Nova\LogResource;
|
||||||
|
use App\Nova\QuestionGroupResource;
|
||||||
use App\Nova\QuestionResource;
|
use App\Nova\QuestionResource;
|
||||||
use App\Nova\ScreeningResource;
|
use App\Nova\ScreeningResource;
|
||||||
use App\Nova\SessionResource;
|
use App\Nova\SessionResource;
|
||||||
@@ -31,10 +32,11 @@ public function boot(): void
|
|||||||
MenuSection::dashboard(Main::class)->icon('home'),
|
MenuSection::dashboard(Main::class)->icon('home'),
|
||||||
|
|
||||||
MenuSection::make('Questionnaire', [
|
MenuSection::make('Questionnaire', [
|
||||||
MenuItem::resource(CategoryResource::class),
|
|
||||||
MenuItem::resource(QuestionResource::class),
|
MenuItem::resource(QuestionResource::class),
|
||||||
MenuItem::resource(ScreeningResource::class),
|
MenuItem::resource(QuestionGroupResource::class),
|
||||||
|
MenuItem::resource(CategoryResource::class),
|
||||||
MenuItem::resource(SessionResource::class),
|
MenuItem::resource(SessionResource::class),
|
||||||
|
MenuItem::resource(ScreeningResource::class),
|
||||||
])->icon('clipboard-document-list')->collapsible(),
|
])->icon('clipboard-document-list')->collapsible(),
|
||||||
|
|
||||||
MenuSection::make('Logs', [
|
MenuSection::make('Logs', [
|
||||||
|
|||||||
52
database/seeders/TestCategorySeeder.php
Normal file
52
database/seeders/TestCategorySeeder.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class TestCategorySeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Seed a minimal test category with one question group and five scored questions for quick testing.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$categoryId = DB::table('categories')->insertGetId([
|
||||||
|
'name' => 'Test',
|
||||||
|
'sort_order' => 99,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$groupId = DB::table('question_groups')->insertGetId([
|
||||||
|
'category_id' => $categoryId,
|
||||||
|
'name' => 'Test Group',
|
||||||
|
'sort_order' => 1,
|
||||||
|
'description' => null,
|
||||||
|
'scoring_instructions' => null,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$questions = [];
|
||||||
|
for ($i = 1; $i <= 5; $i++) {
|
||||||
|
$questions[] = [
|
||||||
|
'question_group_id' => $groupId,
|
||||||
|
'text' => "Test question {$i}",
|
||||||
|
'has_yes' => true,
|
||||||
|
'has_no' => true,
|
||||||
|
'has_na' => true,
|
||||||
|
'details' => 'optional',
|
||||||
|
'sort_order' => $i,
|
||||||
|
'is_scored' => true,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('questions')->insert($questions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,8 +118,8 @@ const validate = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule 3: Text-only questions (no radio buttons, has details)
|
// Rule 3: Text-only questions (no radio buttons, has required details)
|
||||||
if (!hasRadioButtons && question.details && !answer.text_value?.trim()) {
|
if (!hasRadioButtons && question.details && question.details !== 'optional' && !answer.text_value?.trim()) {
|
||||||
errors[question.id] = 'Please enter a response'
|
errors[question.id] = 'Please enter a response'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,9 @@ const completeSession = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
completing = true
|
completing = true
|
||||||
|
clearTimeout(saveTimeout)
|
||||||
router.put(`/sessions/${props.session.id}`, {
|
router.put(`/sessions/${props.session.id}`, {
|
||||||
|
answers: { ...answerData },
|
||||||
complete: true,
|
complete: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user