feat: rename 'name' to 'academic_year' in AcademicTerm and related components for consistency

This commit is contained in:
Yoga Pangestu 2026-09-02 13:20:18 +07:00
parent 6c55fb5f36
commit ded7217dbc
17 changed files with 67 additions and 45 deletions

View File

@ -16,13 +16,13 @@ public function authorize(): bool
public function rules(): array
{
return [
'name' => [
'academic_year' => [
'required',
'string',
'max:20',
function ($attribute, $value, $fail) {
if (! preg_match('/^(\d{4})\/(\d{4})$/', $value, $matches)) {
$fail('Format name harus YYYY/YYYY, contoh: 2025/2026.');
$fail('Format tahun ajaran harus YYYY/YYYY, contoh: 2025/2026.');
return;
}
@ -40,16 +40,16 @@ function ($attribute, $value, $fail) {
'string',
Rule::in(Semester::values()),
Rule::unique('academic_terms')->where(function ($query) {
return $query->where('name', $this->input('name'));
return $query->where('academic_year', $this->input('academic_year'));
})->ignore($this->route('academic_term')),
],
'start_date' => [
'required',
'date',
function ($attribute, $value, $fail) {
$name = $this->input('name');
$academicYear = $this->input('academic_year');
$semester = $this->input('semester');
if ($name && preg_match('/^(\d{4})\/(\d{4})$/', $name, $matches)) {
if ($academicYear && preg_match('/^(\d{4})\/(\d{4})$/', $academicYear, $matches)) {
$startYear = (int) $matches[1];
$endYear = (int) $matches[2];
$dateYear = (int) date('Y', strtotime($value));
@ -65,9 +65,9 @@ function ($attribute, $value, $fail) {
'date',
'after_or_equal:start_date',
function ($attribute, $value, $fail) {
$name = $this->input('name');
$academicYear = $this->input('academic_year');
$semester = $this->input('semester');
if ($name && preg_match('/^(\d{4})\/(\d{4})$/', $name, $matches)) {
if ($academicYear && preg_match('/^(\d{4})\/(\d{4})$/', $academicYear, $matches)) {
$startYear = (int) $matches[1];
$endYear = (int) $matches[2];
$dateYear = (int) date('Y', strtotime($value));

View File

@ -33,7 +33,7 @@ public function paginated(
->select(['id', 'student_id', 'academic_term_id', 'amount_due', 'due_date'])
->withSum('payments as paid_total', 'amount_paid')
->withCount('payments')
->with(['student.user.profile', 'student.department', 'academicTerm:id,name,semester,start_date,end_date'])
->with(['student.user.profile', 'student.department', 'academicTerm:id,academic_year,semester,start_date,end_date'])
->when($status, fn ($q) => match ($status) {
'paid' => $q->havingRaw('COALESCE(paid_total, 0) >= amount_due'),
'partial' => $q->havingRaw('COALESCE(paid_total, 0) > 0 AND COALESCE(paid_total, 0) < amount_due'),

View File

@ -20,7 +20,7 @@ public function paginated(int $perPage = 25, string $search = '', ?int $academic
return CourseClass::query()
->select(['id', 'course_id', 'lecturer_id', 'academic_term_id', 'method'])
->withCount('enrollments')
->with(['course:id,code,name,department_id', 'lecturer.user.profile', 'academicTerm:id,name,semester,start_date,end_date'])
->with(['course:id,code,name,department_id', 'lecturer.user.profile', 'academicTerm:id,academic_year,semester,start_date,end_date'])
->when($search, fn ($q) => $q->whereHas('course', fn ($q) => $q->where('name', 'like', "%{$search}%")->orWhere('code', 'like', "%{$search}%")))
->when($academicTermId, fn ($q) => $q->where('academic_term_id', $academicTermId))
->when($method, fn ($q) => $q->where('method', $method))

View File

@ -113,7 +113,7 @@ public function withDetails(CourseRegistrationSubmission $submission): CourseReg
'student.user.profile',
'student.department.currentLeader.lecturer.user.profile',
'student.academicAdvisor.user.profile',
'academicTerm:id,name,semester,start_date,end_date',
'academicTerm:id,academic_year,semester,start_date,end_date',
'reviewer.profile',
'courseRegistrations.courseClass.course',
'logs' => fn ($q) => $q->oldest(),

View File

@ -11,7 +11,7 @@ class AcademicTermService
{
public function getAllForSelect(): Collection
{
return AcademicTerm::select(['id', 'name', 'semester', 'start_date', 'end_date', 'is_active', 'open_semesters'])->latest()->get();
return AcademicTerm::select(['id', 'academic_year', 'semester', 'start_date', 'end_date', 'is_active', 'open_semesters'])->latest()->get();
}
/**
@ -27,8 +27,8 @@ public function getActive(): ?AcademicTerm
public function paginated(int $perPage = 25, string $search = '', ?string $semester = null, ?bool $isActive = null): LengthAwarePaginator
{
return AcademicTerm::query()
->select(['id', 'name', 'semester', 'start_date', 'end_date', 'is_active', 'open_semesters'])
->when($search, fn($q) => $q->where('name', 'like', "%{$search}%"))
->select(['id', 'academic_year', 'semester', 'start_date', 'end_date', 'is_active', 'open_semesters'])
->when($search, fn($q) => $q->where('academic_year', 'like', "%{$search}%"))
->when($semester, fn($q) => $q->where('semester', $semester))
->when($isActive !== null, fn($q) => $q->where('is_active', $isActive))
->latest()
@ -45,7 +45,7 @@ public function create(array $data): AcademicTerm
public function update(AcademicTerm $academicTerm, array $data): AcademicTerm
{
$academicTerm->name = $data['name'];
$academicTerm->academic_year = $data['academic_year'];
$academicTerm->semester = $data['semester'];
$academicTerm->start_date = $data['start_date'];
$academicTerm->end_date = $data['end_date'];

View File

@ -11,7 +11,7 @@ public function up(): void
{
Schema::create('academic_terms', function (Blueprint $table) {
$table->id();
$table->string('name', 20);
$table->string('academic_year', 20);
$table->enum('semester', Semester::values());
$table->date('start_date');
$table->date('end_date');

View File

@ -12,7 +12,7 @@ public function run(): void
{
AcademicTerm::insert([
[
'name' => '2026/2027',
'academic_year' => '2026/2027',
'semester' => Semester::odd->value,
'start_date' => '2026-08-01',
'end_date' => '2027-01-15',
@ -21,7 +21,7 @@ public function run(): void
'updated_at' => now(),
],
[
'name' => '2026/2027',
'academic_year' => '2026/2027',
'semester' => Semester::even->value,
'start_date' => '2027-02-01',
'end_date' => '2027-07-15',

View File

@ -51,7 +51,7 @@ export function createTuitionInvoiceColumns(
},
},
{
accessorKey: 'academic_term.name',
accessorKey: 'academic_term.academic_year',
header: () => <span>Periode</span>,
cell: ({ row }) => {
const term = row.original.academic_term;

View File

@ -69,7 +69,11 @@ import {
} from '@/types/tuition-payment';
import { createTuitionInvoiceColumns } from './columns';
type AcademicTermOption = { id: number; name: string; semester: string };
type AcademicTermOption = {
id: number;
academic_year: string;
semester: string;
};
type InvoiceSummary = {
unpaid_students: number;

View File

@ -45,7 +45,7 @@ export function createCourseClassColumns(
row.original.lecturer?.user?.profile?.full_name ?? '-',
},
{
accessorKey: 'academic_term.name',
accessorKey: 'academic_term.academic_year',
header: () => <span>Periode</span>,
cell: ({ row }) => {
const term = row.original.academic_term;

View File

@ -46,7 +46,11 @@ type Lecturer = {
departments: { id: number; name: string }[];
user: { profile: { full_name: string } | null } | null;
};
type AcademicTerm = { id: number; name: string; semester: string };
type AcademicTerm = {
id: number;
academic_year: string;
semester: string;
};
type Props = {
courseClasses: {

View File

@ -32,11 +32,11 @@ export function createAcademicTermColumns(
const columns: ColumnDef<AcademicTerm>[] = [
{
accessorKey: 'name',
header: () => <span>Nama</span>,
accessorKey: 'academic_year',
header: () => <span>Tahun Ajaran</span>,
cell: ({ row }) => (
<span className="font-medium">
{row.getValue('name') as string}
{row.getValue('academic_year') as string}
</span>
),
},

View File

@ -182,7 +182,7 @@ export default function AcademicTermIndex({
<DataTable
columns={columns}
data={academicTerms.data}
searchKey="name"
searchKey="academic_year"
pagination={pagination}
onPageChange={handlePageChange}
onPerPageChange={handlePerPageChange}
@ -206,7 +206,7 @@ export default function AcademicTermIndex({
}}
title="Hapus Periode Akademik"
description={(academicTerm) =>
`Apakah Anda yakin ingin menghapus periode akademik "${academicTerm.name}"? Tindakan ini tidak dapat dibatalkan.`
`Apakah Anda yakin ingin menghapus periode akademik "${academicTerm.academic_year}"? Tindakan ini tidak dapat dibatalkan.`
}
onConfirm={handleDelete}
/>
@ -241,15 +241,16 @@ function CreateForm({
{({ errors }) => (
<div className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="name">
Nama <span className="text-destructive">*</span>
<Label htmlFor="academic_year">
Tahun Ajaran{' '}
<span className="text-destructive">*</span>
</Label>
<Input
id="name"
name="name"
placeholder="Masukkan nama periode akademik"
id="academic_year"
name="academic_year"
placeholder="Masukkan tahun ajaran, contoh: 2025/2026"
/>
<InputError message={errors.name} />
<InputError message={errors.academic_year} />
</div>
<div className="grid gap-2">
<Label>
@ -369,16 +370,17 @@ function EditForm({
editing && (
<div className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="edit-name">
Nama <span className="text-destructive">*</span>
<Label htmlFor="edit-academic_year">
Tahun Ajaran{' '}
<span className="text-destructive">*</span>
</Label>
<Input
id="edit-name"
name="name"
placeholder="Masukkan nama periode akademik"
defaultValue={editing.name}
id="edit-academic_year"
name="academic_year"
placeholder="Masukkan tahun ajaran, contoh: 2025/2026"
defaultValue={editing.academic_year}
/>
<InputError message={errors.name} />
<InputError message={errors.academic_year} />
</div>
<div className="grid gap-2">
<Label>

View File

@ -10,7 +10,7 @@ export const SemesterLabels: Record<Semester, string> = {
export type AcademicTerm = {
id: number;
name: string;
academic_year: string;
semester: Semester;
start_date: string;
formatted_start_date: string;
@ -23,10 +23,10 @@ export type AcademicTerm = {
};
export function formatAcademicTermLabel(term: {
name: string;
academic_year: string;
semester: string;
}): string {
const label = SemesterLabels[term.semester as Semester];
return label ? `${term.name} (${label})` : term.name;
return label ? `${term.academic_year} (${label})` : term.academic_year;
}

View File

@ -25,7 +25,11 @@ export type CourseClass = {
user: { profile: { full_name: string } | null } | null;
} | null;
academic_term_id: number;
academic_term: { id: number; name: string; semester: string } | null;
academic_term: {
id: number;
academic_year: string;
semester: string;
} | null;
method: ClassMethod | null;
enrollments_count: number;
created_at: string;

View File

@ -82,7 +82,11 @@ export type CourseRegistrationSubmission = {
advisor_signature_url: string | null;
has_registrations: boolean;
student: CourseRegistrationSubmissionStudent | null;
academic_term: { id: number; name: string; semester: string } | null;
academic_term: {
id: number;
academic_year: string;
semester: string;
} | null;
course_registrations: CourseRegistrationSubmissionEntry[];
logs: CourseRegistrationSubmissionLog[];
};

View File

@ -11,7 +11,11 @@ export type TuitionInvoice = {
student_id: number;
student: TuitionInvoiceStudent | null;
academic_term_id: number;
academic_term: { id: number; name: string; semester: string } | null;
academic_term: {
id: number;
academic_year: string;
semester: string;
} | null;
amount_due: string;
due_date: string | null;
paid_total: string | null;