diff --git a/app/Http/Controllers/DiaryController.php b/app/Http/Controllers/DiaryController.php index ecd49a5..5f37790 100644 --- a/app/Http/Controllers/DiaryController.php +++ b/app/Http/Controllers/DiaryController.php @@ -40,6 +40,7 @@ class DiaryController extends Controller $curDiary->retire = '22:30'; $curDiary->user_id = $curUserId; $curDiary->closed = false; + $curDiary->comment = ""; $curDiary->save(); return $curDiary; diff --git a/app/Http/Controllers/PlanController.php b/app/Http/Controllers/PlanController.php new file mode 100644 index 0000000..0dc6a1c --- /dev/null +++ b/app/Http/Controllers/PlanController.php @@ -0,0 +1,112 @@ +get('author')); */ + if ($request->has('author')) { + $author = $request->get('author'); + $user = User::find($author); + $plans = $user ? $user->author_of_plans : []; + } else { + $plans = Plan::all(); + } + return view('plan/index', ['plans' => $plans]); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + return view('plan/edit_form'); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + * + * @param \App\Models\Plan $plan + * @return \Illuminate\Http\Response + */ + public function show(Plan $plan) + { + $options1 = $this->unserializeOptions($plan->packet1_options); + $options2 = $this->unserializeOptions($plan->packet2_options); + $options3 = $this->unserializeOptions($plan->packet2_options); + return view('plan/show', + [ + 'plan' => $plan, + 'options1' => $options1, + 'options2' => $options2, + 'options3' => $options3 + ] + ); + } + + private function unserializeOptions($str) + { + if (preg_match('/^".*"$/', $str)) { + return json_decode(stripslashes(mb_substr($str, 1, -1)), true); + } + return json_decode(stripslashes($str), true); + } + + /** + * Show the form for editing the specified resource. + * + * @param \App\Models\Plan $plan + * @return \Illuminate\Http\Response + */ + public function edit(Plan $plan) + { + // + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Plan $plan + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Plan $plan) + { + // + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\Plan $plan + * @return \Illuminate\Http\Response + */ + public function destroy(Plan $plan) + { + // + } +} diff --git a/app/Http/Livewire/Plan/Comment/Create.php b/app/Http/Livewire/Plan/Comment/Create.php new file mode 100644 index 0000000..f09ef85 --- /dev/null +++ b/app/Http/Livewire/Plan/Comment/Create.php @@ -0,0 +1,34 @@ +grade = $this->grade; + $comment->body = $this->body; + $comment->plan_id = $this->plan->id; + $comment->user_id = $this->user->id; + $comment->save(); + + $this->reset(['body', 'grade']); + $this->emitTo('plan.comment.show', 'commentAdded'); + } +} diff --git a/app/Http/Livewire/Plan/Comment/Show.php b/app/Http/Livewire/Plan/Comment/Show.php new file mode 100644 index 0000000..72d85db --- /dev/null +++ b/app/Http/Livewire/Plan/Comment/Show.php @@ -0,0 +1,18 @@ + '$refresh']; + + public function render() + { + return view('livewire.plan.comment.show'); + } +} diff --git a/app/Http/Livewire/Plan/Comment/Upvote.php b/app/Http/Livewire/Plan/Comment/Upvote.php new file mode 100644 index 0000000..d25169d --- /dev/null +++ b/app/Http/Livewire/Plan/Comment/Upvote.php @@ -0,0 +1,69 @@ +comment = $comment; + + } + + + public function render() + { + $upvotes = $this->comment->upvotes()->forPlanCommentsUpVote($this->comment)->count(); + $downvotes = $this->comment->upvotes()->forPlanCommentsDownVote($this->comment)->count(); + $sum = $upvotes - $downvotes; + return view( + 'livewire.plan.comment.upvote', + [ + 'sum' => $sum, + 'isUpVoted' => $this->comment->isUpVoted(), + 'isDownVoted' => $this->comment->isDownVoted() + ] + ); + } + + public function upvote() + { + if ($this->comment->isUpVoted()) { + $this->comment->removeUpVote(); + return ; + } + + if ($this->comment->isDownVoted()) { + $this->comment->removeDownVote(); + } + + $this->comment->upvotes()->create([ + 'user_id' => auth()->id(), + 'comment_id' => $this->comment->id, + 'type' => 'upvote' + ]); + } + + public function downvote() + { + if ($this->comment->isDownVoted()) { + $this->comment->removeDownVote(); + return ; + } + + if ($this->comment->isUpVoted()) { + $this->comment->removeUpVote(); + } + + $this->comment->upvotes()->create([ + 'user_id' => auth()->id(), + 'comment_id' => $this->comment->id, + 'type' => 'downvote' + ]); + } +} diff --git a/app/Http/Livewire/Plan/Create.php b/app/Http/Livewire/Plan/Create.php new file mode 100644 index 0000000..cb89689 --- /dev/null +++ b/app/Http/Livewire/Plan/Create.php @@ -0,0 +1,109 @@ + 'required|min:3', + 'image' => 'required|image|max:2048', + 'duration' => 'required|numeric|min:7', + 'state' => 'required|in:draft,active,inactive', + 'type' => ['required', 'array:slim,fit,wellness,behavior', 'min:1'], + 'details' => 'required|min:3', + 'packets_count' => 'required|in:1,3', + 'packet1_options.short' => 'required|string|min:3', + 'packet1_options.price' => 'required|numeric|min:1', + 'packet1_options.description' => 'required|string|min:3', + 'packet1_options.items' => 'required|array|min:1', + ]; + + if($this->packets_count === 3) { + $rules['packet2_options.short'] = 'required|string|min:3'; + $rules['packet2_options.price'] = 'required|numeric|min:1'; + $rules['packet2_options.description'] = 'required|string|min:3'; + $rules['packet2_options.items'] = 'required|array|min:1'; + $rules['packet3_options.short'] = 'required|string|min:3'; + $rules['packet3_options.price'] = 'required|numeric|min:1'; + $rules['packet3_options.description'] = 'required|string|min:3'; + $rules['packet3_options.items'] = 'required|array|min:1'; + } + + return $rules; + } + + public function render() + { + return view('livewire.plan.create'); + } + + public function addPacketOption($options_type, $options_type_input) + { + $this->$options_type['short'] = $this->$options_type_input['short']; + $this->$options_type['price'] = $this->$options_type_input['price']; + $this->$options_type['description'] = $this->$options_type_input['description']; + $this->$options_type['items'][] = $this->input_items; + $this->reset('input_items'); + } + + public function publish() + { + /* dd($this->type); */ + $this->validate(); + + $plan = new Plan(); + $plan->name = $this->name; + $plan->image = $this->image->store('plans', 'public'); + $plan->duration = $this->duration; + $plan->state = $this->state; + $plan->type = json_encode($this->type); + $plan->details = $this->details; + $plan->packets_count = $this->packets_count; + $plan->packet1_options = json_encode($this->packet1_options); + $plan->packet2_options = json_encode($this->packet2_options); + $plan->packet3_options = json_encode($this->packet3_options); + $plan->save(); + $user = Auth::user(); + $user->author_of_plans()->attach($plan->id); + return redirect()->route('plan.show', $plan->id); + } + + public function addToDrafts() + { + $this->type = "draft"; + $this->publish(); + } + + public function showPreview() + { + $this->type = "inactive"; + $this->publish(); + } +} diff --git a/app/Http/Livewire/Plan/Like.php b/app/Http/Livewire/Plan/Like.php new file mode 100644 index 0000000..ed1b526 --- /dev/null +++ b/app/Http/Livewire/Plan/Like.php @@ -0,0 +1,35 @@ +plan = $plan; + } + + public function render() + { + return view('livewire.plan.like'); + } + + public function like(): void + { + if ($this->plan->isLiked()) { + $this->plan->removeLike(); + } elseif (auth()->user()) { + $this->plan->likes()->create([ + 'user_id' => auth()->id(), + 'plan_id' => $this->plan->id + ]); + } + + $this->emitTo('plan.like-count', 'liked'); + } +} diff --git a/app/Http/Livewire/Plan/LikeCount.php b/app/Http/Livewire/Plan/LikeCount.php new file mode 100644 index 0000000..b2cbb2c --- /dev/null +++ b/app/Http/Livewire/Plan/LikeCount.php @@ -0,0 +1,28 @@ + '$refresh']; + + public function mount(Plan $plan) + { + $this->plan = $plan; + } + + public function render() + { + return view( + 'livewire.plan.like-count', + [ + 'count' => $this->plan->likes_count + ] + ); + } +} diff --git a/app/Http/Livewire/Profile/PhotoForm.php b/app/Http/Livewire/Profile/PhotoForm.php index ea05e06..2405f95 100644 --- a/app/Http/Livewire/Profile/PhotoForm.php +++ b/app/Http/Livewire/Profile/PhotoForm.php @@ -38,7 +38,16 @@ class PhotoForm extends Component public function mount() { $this->state = $this->user->withoutRelations()->toArray(); - $lastActivity = DB::table('sessions')->select('last_activity')->where('user_id', $this->user->id)->first()->last_activity; + + $lastActivity = data_get( + DB::table('sessions') + ->select('last_activity') + ->where('user_id', $this->user->id) + ->first(), + 'last_activity', + 0 + ); + $this->isOnline = (now()->unix() - $lastActivity) < 150; } diff --git a/app/Models/CommentUpvote.php b/app/Models/CommentUpvote.php new file mode 100644 index 0000000..9511712 --- /dev/null +++ b/app/Models/CommentUpvote.php @@ -0,0 +1,41 @@ +belongsTo(PlanComment::class, 'comment_id'); + } + + public function author() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function scopeForPlanCommentsUpVote($query, PlanComment $comment) + { + return $query->where('comment_id', $comment->id)->where('type', 'upvote'); + } + + public function scopeForPlanCommentsDownVote($query, PlanComment $comment) + { + return $query->where('comment_id', $comment->id)->where('type', 'downvote'); + } + + +} diff --git a/app/Models/Diary.php b/app/Models/Diary.php index 5b736e3..5ed5b80 100644 --- a/app/Models/Diary.php +++ b/app/Models/Diary.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use App\Models\Meal; +use Illuminate\Database\Eloquent\Casts\Attribute; class Diary extends Model { @@ -28,4 +29,16 @@ class Diary extends Model ->where('user_id', $userId) ->where('date', now()->format('Y-m-d')); } + + public function consultant_comments() + { + return $this->hasMany(ConsultantDiaryComment::class); + } + + protected function hasComments(): Attribute + { + return Attribute::make( + get: fn () => $this->consultant_comments()->count() > 0, + ); + } } diff --git a/app/Models/Plan.php b/app/Models/Plan.php new file mode 100644 index 0000000..5faaca1 --- /dev/null +++ b/app/Models/Plan.php @@ -0,0 +1,57 @@ +belongsToMany(User::class, 'plan_author', 'plan_id', 'user_id'); + } + + public function comments() + { + return $this->hasMany(PlanComment::class); + } + + public function likes(): HasMany + { + return $this->hasMany(PlanLike::class); + } + + public function isLiked(): bool + { + if (auth()->user()) { + return auth()->user()->plan_likes()->forPlan($this)->count(); + } + + return false; + } + + public function removeLike(): bool + { + if (auth()->user()) { + return auth()->user()->plan_likes()->forPlan($this)->delete(); + } + + return false; + } +} diff --git a/app/Models/PlanComment.php b/app/Models/PlanComment.php new file mode 100644 index 0000000..19b3a00 --- /dev/null +++ b/app/Models/PlanComment.php @@ -0,0 +1,81 @@ +belongsTo(User::class, 'user_id'); + } + + public function plan() + { + return $this->belongsTo(Plan::class, 'plan_id'); + } + + public function upvotes(): HasMany + { + return $this->hasMany(CommentUpvote::class, 'comment_id'); + } + + public function isUpVoted(): bool + { + if (auth()->user()) { + return auth() + ->user() + ->comment_upvotes() + ->forPlanCommentsUpVote($this) + ->count(); + } + + return false; + } + + public function isDownVoted(): bool + { + if (auth()->user()) { + return auth() + ->user() + ->comment_upvotes() + ->forPlanCommentsDownVote($this) + ->count(); + } + + return false; + } + + public function removeUpVote(): bool + { + if (auth()->user()) { + return auth() + ->user() + ->comment_upvotes() + ->forPlanCommentsUpVote($this) + ->delete(); + } + + return false; + } + + public function removeDownVote(): bool + { + if (auth()->user()) { + return auth() + ->user() + ->comment_upvotes() + ->forPlanCommentsDownVote($this) + ->delete(); + } + + return false; + } + + +} diff --git a/app/Models/PlanLike.php b/app/Models/PlanLike.php new file mode 100644 index 0000000..f33eddd --- /dev/null +++ b/app/Models/PlanLike.php @@ -0,0 +1,20 @@ +where('plan_id', $plan->id); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index c6d5601..7b71f57 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Notifications\Notifiable; +use Illuminate\Database\Eloquent\Relations\HasMany; use Laravel\Fortify\TwoFactorAuthenticatable; use Laravel\Jetstream\HasProfilePhoto; use Laravel\Sanctum\HasApiTokens; @@ -16,6 +17,9 @@ use App\Models\CircumferenceMeasure; use App\Models\WeightMeasure; use App\Models\ConsultantEducation; use App\Models\UserAttachment; +use App\Models\Plan; +use App\Models\PlanLike; +use App\Models\CommentUpvote; class User extends Authenticatable implements MustVerifyEmail { @@ -109,4 +113,24 @@ class User extends Authenticatable implements MustVerifyEmail ); } + public function author_of_plans() + { + return $this->belongsToMany(Plan::class, 'plan_author', 'user_id', 'plan_id'); + } + + public function plan_comments() + { + return $this->hasMany(PlanComment::class); + } + + public function plan_likes(): HasMany + { + return $this->hasMany(PlanLike::class); + } + + public function comment_upvotes(): HasMany + { + return $this->hasMany(CommentUpvote::class); + } + } diff --git a/config/rich-text-laravel.php b/config/rich-text-laravel.php new file mode 100644 index 0000000..3c1f439 --- /dev/null +++ b/config/rich-text-laravel.php @@ -0,0 +1,35 @@ + \Tonysm\RichTextLaravel\Models\RichText::class, + + /* + |-------------------------------------------------------------------------- + | Supported Files Content-Types + |-------------------------------------------------------------------------- + | + | When attaching non-image files to Trix, you can control here which files + | you want to handle and render in the default "remote file" template + | by explicitly adding your supported Content-Types to this list. + | + */ + 'supported_files_content_types' => [ + 'application/pdf', + 'text/csv', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'text/plain', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ], +]; diff --git a/database/migrations/2022_06_29_135450_create_plans_table.php b/database/migrations/2022_06_29_135450_create_plans_table.php new file mode 100644 index 0000000..f447088 --- /dev/null +++ b/database/migrations/2022_06_29_135450_create_plans_table.php @@ -0,0 +1,41 @@ +id(); + $table->string('name')->comment('name of plan'); + $table->string('image')->nullable()->comment('public path to image'); + $table->smallInteger('duration')->comment('plan duration - 7, 14, 21 days'); + $table->string('state')->comment('active/draft/archived'); + $table->json('type')->comment('to lose weight, to gain weight, to improve your mood, to shape your eating behavior'); + $table->text('details')->comment('plan details'); + $table->tinyInteger('packets_count')->comment('1 on 3 packets of options in plan'); + $table->json('packet1_options'); + $table->json('packet2_options'); + $table->json('packet3_options'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('plans'); + } +}; diff --git a/database/migrations/2022_06_30_090341_create_rich_texts_table.php b/database/migrations/2022_06_30_090341_create_rich_texts_table.php new file mode 100644 index 0000000..31eff54 --- /dev/null +++ b/database/migrations/2022_06_30_090341_create_rich_texts_table.php @@ -0,0 +1,25 @@ +id(); + $table->morphs('record'); + $table->string('field'); + $table->longText('body')->nullable(); + $table->timestamps(); + + $table->unique(['field', 'record_type', 'record_id']); + }); + } + + public function down() + { + Schema::dropIfExists('rich_texts'); + } +}; diff --git a/database/migrations/2022_07_08_083503_create_plan_author.php b/database/migrations/2022_07_08_083503_create_plan_author.php new file mode 100644 index 0000000..c5a9dc9 --- /dev/null +++ b/database/migrations/2022_07_08_083503_create_plan_author.php @@ -0,0 +1,31 @@ +bigInteger('user_id'); + $table->bigInteger('plan_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('plan_author'); + } +}; diff --git a/database/migrations/2022_07_22_105447_create_plan_comments_table.php b/database/migrations/2022_07_22_105447_create_plan_comments_table.php new file mode 100644 index 0000000..f75a027 --- /dev/null +++ b/database/migrations/2022_07_22_105447_create_plan_comments_table.php @@ -0,0 +1,35 @@ +id(); + $table->bigInteger('user_id'); + $table->bigInteger('plan_id'); + $table->smallInteger('grade')->default(0); + $table->longText('body')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('plan_comments'); + } +}; diff --git a/database/migrations/2022_07_25_100927_create_plan_likes_table.php b/database/migrations/2022_07_25_100927_create_plan_likes_table.php new file mode 100644 index 0000000..7e8c36b --- /dev/null +++ b/database/migrations/2022_07_25_100927_create_plan_likes_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('plan_id'); + $table->foreignId('user_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('plan_likes'); + } +}; diff --git a/database/migrations/2022_07_26_111407_create_comment_upvotes_table.php b/database/migrations/2022_07_26_111407_create_comment_upvotes_table.php new file mode 100644 index 0000000..db2097c --- /dev/null +++ b/database/migrations/2022_07_26_111407_create_comment_upvotes_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('comment_id'); + $table->foreignId('user_id'); + $table->string('type')->default('upvote'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('comment_upvotes'); + } +}; diff --git a/eatvio_tmpls/registration.html b/eatvio_tmpls/registration.html index af39195..402621e 100644 --- a/eatvio_tmpls/registration.html +++ b/eatvio_tmpls/registration.html @@ -8,9 +8,9 @@ - + - + @@ -18,7 +18,7 @@
- +
@@ -49,7 +49,7 @@
- + - - +
- +