diff --git a/application/app/DataTransferObjects/ReviewData.php b/application/app/DataTransferObjects/ReviewData.php new file mode 100644 index 0000000000000000000000000000000000000000..5d3935584c335cd9dfd82eb2704361b275457ed7 --- /dev/null +++ b/application/app/DataTransferObjects/ReviewData.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +namespace App\DataTransferObjects; + +use Carbon\Carbon; +use App\Enums\StatusEnum; +use Spatie\LaravelData\Data; +use Spatie\LaravelData\Attributes\Validation\In; +use Spatie\TypeScriptTransformer\Attributes\TypeScript; +use Spatie\TypeScriptTransformer\Attributes\Optional as TypeScriptOptional; + +#[TypeScript] +class ReviewData extends Data +{ + public function __construct( + public ?int $id, + + #[TypeScriptOptional] + public ?int $parent_id, + + #[TypeScriptOptional] + public ?int $user_id, + + public int $model_id, + + public string $model_type, + + #[TypeScriptOptional] + public ?string $title, + + public string $content, + + public ?string $status, + + #[TypeScriptOptional] + public ?Carbon $published_at, + + public string $type, + + public int $ranking_total, + + public int $helpful_total, + + public int $not_helpful_total, + ) {} +} diff --git a/application/app/Enums/PermissionEnum.php b/application/app/Enums/PermissionEnum.php index 86912ba5f4687b56079da1700ec1d989d8ddf17c..4e4660332edfd3e60fce0011dbcc5dca3f11ce0f 100644 --- a/application/app/Enums/PermissionEnum.php +++ b/application/app/Enums/PermissionEnum.php @@ -57,8 +57,5 @@ */ final class PermissionEnum extends Enum { - protected static function values(): \Closure - { - return fn(string $name): string|int => str_replace('_', ' ', mb_strtolower($name)); - } + use Traits\HasValues; } diff --git a/application/app/Enums/RoleEnum.php b/application/app/Enums/RoleEnum.php index 7385757e57119920f64ada93da000dccce0c4453..8e47e7e982a020a875010bd80dda97faf006e130 100644 --- a/application/app/Enums/RoleEnum.php +++ b/application/app/Enums/RoleEnum.php @@ -15,8 +15,5 @@ */ final class RoleEnum extends Enum { - protected static function values(): \Closure - { - return fn(string $name): string|int => str_replace('_', ' ', mb_strtolower($name)); - } + use Traits\HasValues; } diff --git a/application/app/Enums/StatusEnum.php b/application/app/Enums/StatusEnum.php new file mode 100644 index 0000000000000000000000000000000000000000..d6450e23abbccd58c99fb4f5024ff97ebad277e4 --- /dev/null +++ b/application/app/Enums/StatusEnum.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace App\Enums; + +use Spatie\Enum\Laravel\Enum; + +/** + * @method static self draft() + * @method static self pending() + * @method static self published() + */ +class StatusEnum extends Enum +{ + use Traits\HasValues; +} diff --git a/application/app/Enums/Traits/HasValues.php b/application/app/Enums/Traits/HasValues.php new file mode 100644 index 0000000000000000000000000000000000000000..caf0dde1e8211b523be48b1f48eaada985dec87f --- /dev/null +++ b/application/app/Enums/Traits/HasValues.php @@ -0,0 +1,11 @@ +<?php + +namespace App\Enums\Traits; + +trait HasValues +{ + public static function values(): \Closure + { + return fn(string $name): string|int => str_replace('_', ' ', mb_strtolower($name)); + } +} diff --git a/application/app/Models/Review.php b/application/app/Models/Review.php new file mode 100644 index 0000000000000000000000000000000000000000..6a40320f7f2c42436fc329c36bac975d10e9e7bb --- /dev/null +++ b/application/app/Models/Review.php @@ -0,0 +1,8 @@ +<?php + +namespace App\Models; + +use App\Models\Model; + + +class Review extends Model {} diff --git a/application/app/Models/ReviewModeration.php b/application/app/Models/ReviewModeration.php index 5243f8cbc11bf2664ee9e51694da473578d69b1b..9ac362e50e614f5629d7eb15a0f4420cba21d857 100644 --- a/application/app/Models/ReviewModeration.php +++ b/application/app/Models/ReviewModeration.php @@ -4,10 +4,6 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; +use App\Models\Model; -class ReviewModeration extends Model -{ - use HasFactory; -} +class ReviewModeration extends Model {} diff --git a/application/app/Policies/ReviewModerationPolicy.php b/application/app/Policies/ReviewModerationPolicy.php index bfbd34513950e054e24f2ef290867e8ac6d6a432..688a1b19c08f924649aca7839a074c059f9bc400 100644 --- a/application/app/Policies/ReviewModerationPolicy.php +++ b/application/app/Policies/ReviewModerationPolicy.php @@ -5,9 +5,7 @@ namespace App\Policies; use App\Models\User; -use App\Enums\RoleEnum; use App\Enums\PermissionEnum; -use Illuminate\Auth\Access\Response; class ReviewModerationPolicy extends AppPolicy { @@ -16,7 +14,7 @@ class ReviewModerationPolicy extends AppPolicy */ public function viewAny(User $user): bool { - return parent::canViewAny($user) || $user->hasAnyPermission([PermissionEnum::read_users()->value]); + return parent::canViewAny($user); } /** @@ -24,7 +22,7 @@ public function viewAny(User $user): bool */ public function view(User $user, User $model): bool { - return parent::canView($user, $model) || $user->hasAnyPermission([PermissionEnum::read_users()->value]); + return parent::canView($user, $model); } /** @@ -32,7 +30,7 @@ public function view(User $user, User $model): bool */ public function create(User $user): bool { - return parent::canCreate($user) || $user->hasAnyPermission([PermissionEnum::create_users()->value]); + return parent::canCreate($user); } /** @@ -40,7 +38,7 @@ public function create(User $user): bool */ public function update(User $user, User $model): bool { - return parent::canUpdate($user, $model) || $user->hasAnyPermission([PermissionEnum::update_users()->value]); + return parent::canUpdate($user, $model); } /** @@ -48,6 +46,6 @@ public function update(User $user, User $model): bool */ public function delete(User $user, User $model): bool { - return parent::canDelete($user, $model) || $user->hasAnyPermission([PermissionEnum::delete_users()->value]); + return parent::canDelete($user, $model); } } diff --git a/application/app/Policies/ReviewPolicy.php b/application/app/Policies/ReviewPolicy.php new file mode 100644 index 0000000000000000000000000000000000000000..f323cc421a4d2fb54c64fc353b19d1161a5e7929 --- /dev/null +++ b/application/app/Policies/ReviewPolicy.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +namespace App\Policies; + +use App\Models\User; +use App\Enums\PermissionEnum; + +class ReviewPolicy extends AppPolicy +{ + /** + * Determine whether the user can view any models. + */ + public function viewAny(User $user): bool + { + return parent::canViewAny($user) || $user->hasAnyPermission([PermissionEnum::read_reviews()->value]); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, User $model): bool + { + return parent::canView($user, $model) || $user->hasAnyPermission([PermissionEnum::read_reviews()->value]); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return parent::canCreate($user) || $user->hasAnyPermission([PermissionEnum::create_reviews()->value]); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, User $model): bool + { + return parent::canUpdate($user, $model) || $user->hasAnyPermission([PermissionEnum::update_reviews()->value]); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, User $model): bool + { + return parent::canDelete($user, $model) || $user->hasAnyPermission([PermissionEnum::delete_reviews()->value]); + } +} diff --git a/application/database/factories/ReviewFactory.php b/application/database/factories/ReviewFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7ff978f545b0a31defbf595651e4ba4f6adf8e16 --- /dev/null +++ b/application/database/factories/ReviewFactory.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +namespace Database\Factories; + +use App\Models\User; +use App\Models\Review; +use App\Models\Proposal; +use App\Enums\StatusEnum; +use Illuminate\Database\Eloquent\Factories\Factory; + +class ReviewFactory extends Factory +{ + protected $model = Review::class; + + public function definition() + { + return [ + 'parent_id' => null, + 'user_id' => User::factory(), + 'model_id' => Proposal::factory(), + 'model_type' => Proposal::class, + 'title' => $this->faker->sentence(), + 'content' => $this->faker->paragraph(), + 'status' => $this->faker->randomElement(StatusEnum::toArray()), + 'published_at' => $this->faker->optional()->dateTime(), + 'type' => 'App\Models\Comment', + 'ranking_total' => $this->faker->numberBetween(0, 100), + 'helpful_total' => $this->faker->numberBetween(0, 100), + 'not_helpful_total' => $this->faker->numberBetween(0, 100), + ]; + } + + /** + * Indicate that the review is published. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function published() + { + return $this->state([ + 'status' => StatusEnum::approved()->value, + 'published_at' => now(), + ]); + } +} diff --git a/application/database/factories/ReviewModerationFactory.php b/application/database/factories/ReviewModerationFactory.php index 07e277ee23c424feebfceba98923222782b23609..169446416e01edecbd828e9ee135bc96d2f94fd8 100644 --- a/application/database/factories/ReviewModerationFactory.php +++ b/application/database/factories/ReviewModerationFactory.php @@ -1,5 +1,7 @@ <?php +declare(strict_types=1); + namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; diff --git a/application/database/migrations/2024_11_12_182901_create_reviews_table.php b/application/database/migrations/2024_11_12_182901_create_reviews_table.php new file mode 100644 index 0000000000000000000000000000000000000000..4977a217de18ed5d1b2d42c3c4159f092d6bfd59 --- /dev/null +++ b/application/database/migrations/2024_11_12_182901_create_reviews_table.php @@ -0,0 +1,40 @@ +<?php + +use App\Enums\StatusEnum; +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +return new class extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('reviews', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + $table->bigInteger('parent_id')->nullable(); + $table->foreignId('user_id')->constrained('users')->nullable(); + $table->bigInteger('model_id'); + $table->string('model_type'); + $table->string('title')->nullable(); + $table->text('content'); + $table->enum('status', StatusEnum::toArray())->default(StatusEnum::pending()->value)->nullable; + $table->timestamp('published_at')->nullable(); + $table->char('type')->default('App\Models\Comment'); + $table->integer('ranking_total')->default(0); + $table->integer('helpful_total')->default(0); + $table->integer('not_helpful_total')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('reviews'); + } +}; diff --git a/application/database/seeders/ReviewSeeder.php b/application/database/seeders/ReviewSeeder.php new file mode 100644 index 0000000000000000000000000000000000000000..1924adbd805a1163ace8ff5db6ced9a9f9744b17 --- /dev/null +++ b/application/database/seeders/ReviewSeeder.php @@ -0,0 +1,18 @@ +<?php + +namespace Database\Seeders; + +use App\Models\Review; +use Illuminate\Database\Seeder; +use Illuminate\Database\Console\Seeds\WithoutModelEvents; + +class ReviewSeeder extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + Review::factory(10)->create(); + } +} diff --git a/application/resources/types/generated.d.ts b/application/resources/types/generated.d.ts index 975763350ba1f271e36338dcb46d6146d7f3b9a4..d4b4ef8692bab8514bc8b463b64568c8dbc7e346 100644 --- a/application/resources/types/generated.d.ts +++ b/application/resources/types/generated.d.ts @@ -1,4 +1,20 @@ declare namespace App.DataTransferObjects { + export type GroupData = { + id: number | null; + user_id?: number; + name?: string; + bio?: Array<any>; + slug?: string; + status?: string; + meta_title?: string; + website?: string; + twitter?: string; + discord?: string; + github?: string; + created_at?: string; + updated_at?: string; + deleted_at?: string; + }; export type IdeascaleProfileData = { id: number | null; ideascaleId?: number; @@ -53,6 +69,21 @@ declare namespace App.DataTransferObjects { quickpitch?: string; quickpitch_length?: number; }; + export type ReviewData = { + id: number | null; + parent_id?: number; + user_id?: number; + model_id: number; + model_type: string; + title?: string; + content: string; + status: string | null; + published_at?: string; + type: string; + ranking_total: number; + helpful_total: number; + not_helpful_total: number; + }; export type ReviewModerationData = { id: number | null; reviewer_id?: number;