diff --git a/app/Http/Requests/Profile/UpdateProfileRequest.php b/app/Http/Requests/Profile/UpdateProfileRequest.php index 867d782..a503ee0 100644 --- a/app/Http/Requests/Profile/UpdateProfileRequest.php +++ b/app/Http/Requests/Profile/UpdateProfileRequest.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; class UpdateProfileRequest extends FormRequest { @@ -24,6 +25,14 @@ public function rules(): array { return [ 'name' => ['required', 'string', 'max:255'], + 'username' => [ + 'sometimes', + 'string', + 'min:3', + 'max:30', + 'regex:/^[a-zA-Z0-9_]+$/', + Rule::unique('users', 'username')->ignore($this->user()->id), + ], 'file' => ['sometimes', 'nullable', 'image', 'max:2048'], 'about' => ['sometimes', 'nullable', 'string'], 'institution' => ['sometimes', 'nullable', 'string', 'max:255'], diff --git a/app/Models/User.php b/app/Models/User.php index 206f211..3417c47 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -38,6 +38,7 @@ class User extends Authenticatable protected $fillable = [ 'name', + 'username', 'email', 'receive_emails', 'google_id', @@ -50,6 +51,15 @@ class User extends Authenticatable 'instagram', 'github', ]; + protected static function booted(): void + { + static::created(function (User $user) { + if (! $user->username) { + $user->updateQuietly(['username' => "student_{$user->id}"]); + $user->username = "student_{$user->id}"; + } + }); + } protected $appends = [ 'image_url', diff --git a/database/migrations/2026_08_26_120400_add_username_to_users_table.php b/database/migrations/2026_08_26_120400_add_username_to_users_table.php new file mode 100644 index 0000000..fc37b18 --- /dev/null +++ b/database/migrations/2026_08_26_120400_add_username_to_users_table.php @@ -0,0 +1,33 @@ +string('username')->nullable()->unique()->after('name'); + }); + + User::whereNull('username')->each(function ($user) { + $user->updateQuietly(['username' => "student_{$user->id}"]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('username'); + }); + } +}; diff --git a/resources/js/pages/Profile.vue b/resources/js/pages/Profile.vue index 7ab6e00..fe1696d 100644 --- a/resources/js/pages/Profile.vue +++ b/resources/js/pages/Profile.vue @@ -32,6 +32,7 @@ const showConfirmModal = ref(false); const form = useForm({ _method: 'PUT', name: user.value?.name || '', + username: user.value?.username || '', file: null as File | null, about: user.value?.about || '', institution: user.value?.institution || '', @@ -178,6 +179,47 @@ const submitForm = () => {
++ {{ form.errors.username }} +
++ Letters, numbers, and underscores (3–30 chars). +
+