feat: userProfile
This commit is contained in:
parent
4e8664ee31
commit
8f24453eff
28 changed files with 457 additions and 113 deletions
|
@ -23,7 +23,6 @@ class LoginUser
|
|||
try {
|
||||
$user = User::where('login', $userInfo['username'])->first();
|
||||
if ($user) {
|
||||
$user['role'] = ($user->getRoleNames())->first();
|
||||
$password = $user->getAuthPassword();
|
||||
if (strlen($password) === 32 && ctype_xdigit($password)) {
|
||||
if (hash_equals($password, md5($userInfo['password']))) {
|
||||
|
|
|
@ -29,7 +29,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||
];
|
||||
|
||||
// Only add the 'type' validation rule if the user has the permission to change roles
|
||||
if (auth()->user()->hasPermissionTo('user.changeRole')) {
|
||||
if (auth()->user()->hasPermissionTo('users.changeRole')) {
|
||||
$rules['type'] = ['required', 'string', 'max:6', Rule::in(['admin', 'editor', 'dj'])];
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||
];
|
||||
|
||||
// Only update 'type' if the user has the permission
|
||||
if (auth()->user()->hasPermissionTo('user.changeRole')) {
|
||||
if (auth()->user()->hasPermissionTo('users.changeRole')) {
|
||||
$data['type'] = $input['type'];
|
||||
}
|
||||
|
||||
|
|
12
app/Filters/FiltersType/GreaterEqualFilter.php
Normal file
12
app/Filters/FiltersType/GreaterEqualFilter.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\FiltersType;
|
||||
|
||||
class GreaterEqualFilter
|
||||
{
|
||||
function __invoke($query, $whereToSearch, $textToSearch) {
|
||||
return $query->where(function($query) use ($whereToSearch, $textToSearch) {
|
||||
$query->where($whereToSearch, '>=', $textToSearch);
|
||||
});
|
||||
}
|
||||
}
|
12
app/Filters/FiltersType/SmallerEqualFiler.php
Normal file
12
app/Filters/FiltersType/SmallerEqualFiler.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\FiltersType;
|
||||
|
||||
class SmallerEqualFiler
|
||||
{
|
||||
function __invoke($query, $whereToSearch, $textToSearch) {
|
||||
return $query->where(function($query) use ($whereToSearch, $textToSearch) {
|
||||
$query->where($whereToSearch, '<=', $textToSearch);
|
||||
});
|
||||
}
|
||||
}
|
42
app/Filters/ScheduleFilters.php
Normal file
42
app/Filters/ScheduleFilters.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Filters\FiltersType\AllFilter;
|
||||
use App\Filters\FiltersType\GreaterEqualFilter;
|
||||
use App\Filters\FiltersType\IsFilter;
|
||||
use App\Filters\FiltersType\LikeFilter;
|
||||
use App\Filters\FiltersType\SmallerEqualFiler;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ScheduleFilters
|
||||
{
|
||||
protected $filters = [
|
||||
'ends' => SmallerEqualFiler::class,
|
||||
'starts' => GreaterEqualFilter::class,
|
||||
];
|
||||
|
||||
public function apply($query, Request $filters)
|
||||
{
|
||||
foreach ($this->receivedFilters($filters) as $name => $value) {
|
||||
switch ($name) {
|
||||
case 'all':
|
||||
$name = array_diff(array_keys($this->filters), ['all']);
|
||||
$filterInstance = new $this->filters['all'];
|
||||
break;
|
||||
default:
|
||||
$filterInstance = new $this->filters[$name];
|
||||
break;
|
||||
}
|
||||
$query = $filterInstance($query, $name, $value);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
public function receivedFilters(Request $filters)
|
||||
{
|
||||
return $filters->only(array_keys($this->filters));
|
||||
}
|
||||
}
|
|
@ -5,12 +5,15 @@ namespace App\Http\Controllers;
|
|||
use App\Http\Requests\ScheduleRequest;
|
||||
use App\Http\Resources\ScheduleResource;
|
||||
use App\Models\Schedule;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ScheduleController extends Controller
|
||||
{
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
return ScheduleResource::collection(Schedule::all());
|
||||
$schedule = Schedule::searchFilter($request)->get();
|
||||
|
||||
return $schedule->toJson();
|
||||
}
|
||||
|
||||
public function store(ScheduleRequest $request)
|
||||
|
|
|
@ -2,30 +2,25 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Filters\Show\ShowFilters;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ShowRequest;
|
||||
use App\Http\Resources\ShowResource;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
use App\Models\Show\Show;
|
||||
use App\Models\User;
|
||||
use App\Traits\ScheduleTrait;
|
||||
use App\Traits\Show\ShowDaysTrait;
|
||||
use App\Traits\Show\ShowDjTrait;
|
||||
use App\Traits\Show\ShowInstancesTrait;
|
||||
use App\Traits\Show\ShowTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Exception;
|
||||
use Log;
|
||||
|
||||
# TODO Expose the show instance generation for user interaction and pypo queue
|
||||
# When pypo requests the schedule up to a certain date, generate the shows up to that date
|
||||
class UserController extends Controller
|
||||
{
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$queryParams = collect($request->except('withShow'));
|
||||
$userFilter = (new User())->searchFilter($queryParams);
|
||||
if($request->withShow) $userFilter = $userFilter->with('showDjs');
|
||||
$userFilter = (new User())->searchFilter($queryParams);
|
||||
if ($request->withShow) {
|
||||
$userFilter = $userFilter->with('showDjs');
|
||||
}
|
||||
|
||||
return response()->json($userFilter->get());
|
||||
}
|
||||
|
||||
|
@ -41,24 +36,54 @@ class UserController extends Controller
|
|||
$show = Show::firstOrCreate($showInfos);
|
||||
$this->manageShowDays($show, $showDaysRules);
|
||||
$this->manageShowDjs($showDjs, $show);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Show created successfully']);
|
||||
}
|
||||
|
||||
public function show(ShowResource $show)
|
||||
public function show(User $user)
|
||||
{
|
||||
return new ShowResource($show);
|
||||
$allowedRoles = ['admin', 'editor'];
|
||||
$authenticatedUser = auth()->user();
|
||||
if ( ! $authenticatedUser && ! in_array($authenticatedUser->type, $allowedRoles)) {
|
||||
return response()->json(['message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
return response()->json($user);
|
||||
}
|
||||
|
||||
public function update(ShowRequest $request, Show $show)
|
||||
public function userProfile()
|
||||
{
|
||||
$show->update($request->validated());
|
||||
|
||||
return new ShowResource($show);
|
||||
return response()->json(auth()->user());
|
||||
}
|
||||
|
||||
public function update(Request $request, User $user, UpdateUserProfileInformation $updater)
|
||||
{
|
||||
$authenticatedUser = auth()->user();
|
||||
|
||||
if ($authenticatedUser->id !== $user->id && ! $authenticatedUser->hasPermissionTo('user.manageAll')) {
|
||||
return response()->json(['message' => 'You do not have permission to edit other users.'], 403);
|
||||
}
|
||||
if ($authenticatedUser->id === $user->id && ! $authenticatedUser->hasPermissionTo('users.manageOwn')) {
|
||||
return response()->json(['message' => 'You do not have permission to edit your own profile.'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
(new UpdateUserProfileInformation())->update($user, $request->all());
|
||||
|
||||
$user->load('preferences');
|
||||
|
||||
return response()->json($user);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
return response()->json(['message' => 'Failed to update user'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
try {
|
||||
|
@ -71,10 +96,4 @@ class UserController extends Controller
|
|||
|
||||
return response()->json(['message' => $responseMessage]);
|
||||
}
|
||||
|
||||
public function testSchedule(int $showId)
|
||||
{
|
||||
$show = Show::find($showId);
|
||||
$this->manageShowSchedule($show);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@ class Authenticate extends Middleware
|
|||
*/
|
||||
protected function redirectTo(Request $request): ?string
|
||||
{
|
||||
return $request->expectsJson() ? null : route('login');
|
||||
return $request->expectsJson() ? null : '/login';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,18 @@ class LoginResponse implements LoginResponseContract
|
|||
public function toResponse($request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$user->load(['roles', 'preferences' => function ($query) {
|
||||
$query->where('keystr', 'user_timezone');
|
||||
}]);
|
||||
|
||||
$timezonePreference = $user->preferences->first();
|
||||
$user->timezone = $timezonePreference ? $timezonePreference->value : null;
|
||||
unset($user->preferences);
|
||||
|
||||
$user->role = $user->roles->first() ? $user->roles->first()->name : null;
|
||||
unset($user->roles);
|
||||
|
||||
return response()->json($user);
|
||||
}
|
||||
}
|
||||
|
|
20
app/Http/Responses/LogoutResponse.php
Normal file
20
app/Http/Responses/LogoutResponse.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Responses;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Laravel\Fortify\Contracts\LogoutResponse as LogoutResponseContract;
|
||||
|
||||
class LogoutResponse implements LogoutResponseContract
|
||||
{
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function toResponse($request)
|
||||
{
|
||||
return $request->wantsJson()
|
||||
? new JsonResponse('', 204)
|
||||
: redirect('/login');
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Filters\ScheduleFilters;
|
||||
use App\Models\ShowInstances\ShowInstances;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
@ -49,4 +50,9 @@ class Schedule extends Model
|
|||
{
|
||||
return $this->belongsTo(ShowInstances::class, 'show_instance_id');
|
||||
}
|
||||
public function scopeSearchFilter($query, $request)
|
||||
{
|
||||
$filters = new ScheduleFilters();
|
||||
return $filters->apply($query, $request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Filters\UserFilters;
|
||||
use App\Helpers\Preferences;
|
||||
use App\Models\Show\ShowHosts;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
@ -26,10 +27,16 @@ class User extends Authenticatable
|
|||
protected $fillable = [
|
||||
'login',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'cell_phone',
|
||||
'pass',
|
||||
'type'
|
||||
'type',
|
||||
'timezone',
|
||||
];
|
||||
|
||||
protected $appends = ['timezone'];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
|
@ -87,6 +94,24 @@ class User extends Authenticatable
|
|||
parent::setAttribute($key, $value);
|
||||
}
|
||||
|
||||
public function getTimezoneAttribute(): string
|
||||
{
|
||||
// Find the timezone preference or return a default value.
|
||||
return $this->preferences()->where('keystr', 'user_timezone')->first()->valstr ?? Preferences::getDefaultTimeZone();
|
||||
}
|
||||
|
||||
public function setTimezoneAttribute(?string $value): void
|
||||
{
|
||||
if ($value) {
|
||||
$this->preferences()->updateOrCreate(
|
||||
['keystr' => 'user_timezone'],
|
||||
['valstr' => $value]
|
||||
);
|
||||
} else {
|
||||
$this->preferences()->where('keystr', 'user_timezone')->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the password column for authentication.
|
||||
*/
|
||||
|
@ -113,4 +138,9 @@ class User extends Authenticatable
|
|||
{
|
||||
return $this->hasMany(ShowHosts::class, 'subjs_id');
|
||||
}
|
||||
|
||||
public function preferences()
|
||||
{
|
||||
return $this->hasMany(Preference::class, 'subjid');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use Illuminate\Support\ServiceProvider;
|
|||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\Fortify;
|
||||
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
|
||||
use Laravel\Fortify\Contracts\LogoutResponse;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
@ -32,11 +33,20 @@ class FortifyServiceProvider extends ServiceProvider
|
|||
public function boot(): void
|
||||
{
|
||||
$this->app->singleton(LoginResponseContract::class, LoginResponse::class);
|
||||
$this->app->singleton(LogoutResponse::class, LogoutResponse::class);
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
||||
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
||||
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
||||
Fortify::authenticateUsing([LoginUser::class, 'login']);
|
||||
$this->app->instance(LogoutResponse::class, new class implements LogoutResponse {
|
||||
public function toResponse($request)
|
||||
{
|
||||
return redirect('/login');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())) . '|' . $request->ip());
|
||||
|
|
|
@ -29,6 +29,6 @@ return [
|
|||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => false,
|
||||
'supports_credentials' => true,
|
||||
|
||||
];
|
||||
|
|
|
@ -10,7 +10,7 @@ import CalendarShowEvent from "@partials/show/CalendarShowEvent.vue"
|
|||
|
||||
// Store
|
||||
const auth = useAuthStore();
|
||||
const userRole = auth.userData.user.role;
|
||||
const userRole = auth.userData.role;
|
||||
|
||||
// Data
|
||||
const editMode = ref(false);
|
||||
|
|
91
resources/js/components/content/UserProfile.vue
Normal file
91
resources/js/components/content/UserProfile.vue
Normal file
|
@ -0,0 +1,91 @@
|
|||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth.store.ts'; // Adjust the import path to your store
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const { userData } = storeToRefs(authStore);
|
||||
|
||||
const emit = defineEmits([
|
||||
'userProfilePage'
|
||||
])
|
||||
|
||||
// Sample data for selection components
|
||||
const roles = ['Admin', 'User', 'Editor'];
|
||||
const timezones = [
|
||||
'UTC',
|
||||
'America/New_York',
|
||||
'America/Chicago',
|
||||
'America/Denver',
|
||||
'America/Los_Angeles',
|
||||
'Europe/London',
|
||||
'Europe/Berlin',
|
||||
'Asia/Tokyo',
|
||||
];
|
||||
|
||||
const form = ref(null);
|
||||
|
||||
const saveUser = async () => {
|
||||
await authStore.updateUser();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-form ref="form">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="userData.login"
|
||||
label="Login"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="userData.firstName"
|
||||
label="First Name"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="userData.lastName"
|
||||
label="Last Name"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-autocomplete
|
||||
v-model="userData.timezone"
|
||||
:items="timezones"
|
||||
label="Timezone"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="userData.email"
|
||||
label="email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="userData.cellPhone"
|
||||
label="cellPhone"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn color="primary" @click="saveUser">
|
||||
Salva
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</template>
|
|
@ -76,7 +76,7 @@ function hiddenSmartBlockCriteria(){
|
|||
|
||||
// Hook and watch
|
||||
onMounted(() => {
|
||||
smartBlockStore.updateField({key: 'creator_id', value: auth.userData.user.id})
|
||||
smartBlockStore.updateField({key: 'creator_id', value: auth.userData.id})
|
||||
hiddenSmartBlockCriteria()
|
||||
})
|
||||
|
||||
|
|
|
@ -11,19 +11,17 @@ const color = ref("secondary");
|
|||
|
||||
const isOnAir = async (): void => {
|
||||
const now = DateTime.now().setZone(import.meta.env.VITE_APP_TIMEZONE).toISO();
|
||||
return await axios.get(`/api/v2/schedule`, {
|
||||
auth: {
|
||||
username: auth.userData.user.login,
|
||||
password: auth.userData.password
|
||||
},
|
||||
return await axios.get(`/schedule`, {
|
||||
params: {
|
||||
ends_after: now,
|
||||
starts_before: now,
|
||||
ends: now,
|
||||
start: now,
|
||||
}
|
||||
}).then((response: AxiosResponse) => {
|
||||
if (typeof response.data === Array && response.data.length > 0) {
|
||||
color.value = 'error';
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import {useRouter} from "vue-router";
|
||||
import {useAuthStore} from "@/stores/auth.store.ts";
|
||||
import axios from "axios";
|
||||
|
||||
const router = useRouter();
|
||||
const auth = useAuthStore();
|
||||
const userInfo = auth.userData;
|
||||
if (!userInfo) {
|
||||
router.push('/login');
|
||||
}
|
||||
const userName = auth.userData.login;
|
||||
|
||||
const logout = () => {
|
||||
auth.logout();
|
||||
router.push('/login');
|
||||
const emit = defineEmits([
|
||||
'userProfilePage'
|
||||
])
|
||||
|
||||
const userProfilePage = () => {
|
||||
emit('userProfilePage')
|
||||
}
|
||||
const logout = async () => {
|
||||
try {
|
||||
await axios.get('/logout');
|
||||
auth.resetUser();
|
||||
router.push('/login');
|
||||
} catch (error) {
|
||||
console.error('Error logging out:', error);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -20,16 +30,17 @@ const logout = () => {
|
|||
<v-sheet
|
||||
:width="150"
|
||||
>
|
||||
<v-btn color="info">{{ userName }} {{ $t('header.userinfo.info') }}</v-btn>
|
||||
<v-btn color="info" @click="userProfilePage">{{ userName }} {{ $t('header.userinfo.info') }}</v-btn>
|
||||
<v-btn color="" @click="logout">{{ $t('header.userinfo.logout') }}</v-btn>
|
||||
</v-sheet>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div button {
|
||||
width: 100%;
|
||||
}
|
||||
div button:not(:first-child) {
|
||||
margin-top: 2px;
|
||||
}
|
||||
div button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div button:not(:first-child) {
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
|
@ -9,6 +9,7 @@ export interface User {
|
|||
lastName: string;
|
||||
email?: boolean;
|
||||
cellPhone?: boolean;
|
||||
timezone?: string;
|
||||
|
||||
show?: Show[];
|
||||
}
|
||||
|
|
|
@ -18,19 +18,18 @@ const router = createRouter({
|
|||
history: createWebHistory(),
|
||||
routes
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
/**
|
||||
* Redirect to login page if unauthenticated
|
||||
*/
|
||||
router.beforeEach(async (to) => {
|
||||
// redirect to login page if not logged in and trying to access a restricted page
|
||||
const publicPages = ['/login'];
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
const auth = useAuthStore();
|
||||
|
||||
if (authRequired && !auth.userData) {
|
||||
auth.returnUrl = to.fullPath;
|
||||
if (authRequired && !auth.userData.login) {
|
||||
return '/login';
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ const tabs = {
|
|||
spot: defineAsyncComponent(() => import('@components/content/Show.vue')),
|
||||
'spot-playlist': defineAsyncComponent(() => import('@components/content/Playlist.vue')),
|
||||
'spot-blocks': defineAsyncComponent(() => import('@components/content/SmartBlock.vue')),
|
||||
userProfile: defineAsyncComponent(() => import('@components/content/UserProfile.vue')),
|
||||
}
|
||||
|
||||
watch(currentPage, (newVal) => {
|
||||
|
|
|
@ -3,6 +3,14 @@ import OnAir from "@/components/header/OnAir.vue";
|
|||
import Clock from "@/components/header/Clock.vue";
|
||||
import Timer from "@/components/header/Timer.vue";
|
||||
import UserInfo from "@/components/header/UserInfo.vue";
|
||||
|
||||
const emit = defineEmits([
|
||||
'userProfilePage'
|
||||
])
|
||||
|
||||
const userProfilePage = () => {
|
||||
emit('userProfilePage')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -10,7 +18,7 @@ import UserInfo from "@/components/header/UserInfo.vue";
|
|||
<OnAir />
|
||||
<Clock />
|
||||
<Timer />
|
||||
<UserInfo />
|
||||
<UserInfo @user-profile-page="userProfilePage" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -10,6 +10,19 @@ const page = reactive({
|
|||
name: 'Dashboard',
|
||||
})
|
||||
|
||||
let returnToPage = {id: '', name: ''}
|
||||
|
||||
const userProfilePage = () => {
|
||||
if(page.id == 'userProfile') {
|
||||
page.id = returnToPage.id
|
||||
page.name = returnToPage.name
|
||||
return
|
||||
}
|
||||
returnToPage = {...page}
|
||||
page.id = 'userProfile'
|
||||
page.name = 'userProfile'
|
||||
}
|
||||
|
||||
const changePage = (currentPage) => {
|
||||
page.id = currentPage.id;
|
||||
page.name = currentPage.name;
|
||||
|
@ -18,20 +31,20 @@ const changePage = (currentPage) => {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<Header />
|
||||
<Header @user-profile-page="userProfilePage" />
|
||||
<v-row :fluid="true">
|
||||
<Sidebar
|
||||
@show-page="changePage"
|
||||
@show-page="changePage"
|
||||
/>
|
||||
<Content
|
||||
:page="page"
|
||||
:page="page"
|
||||
/>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-row {
|
||||
margin: 0;
|
||||
}
|
||||
.v-row {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,57 +1,58 @@
|
|||
<script setup lang="ts">
|
||||
import {reactive} from 'vue';
|
||||
import {onBeforeMount, reactive} from 'vue';
|
||||
import axios from "axios";
|
||||
import {useRouter} from "vue-router";
|
||||
import { Settings } from "luxon";
|
||||
import {Settings} from "luxon";
|
||||
import {useAuthStore} from "@/stores/auth.store.ts";
|
||||
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
const data = reactive({
|
||||
'username': null,
|
||||
'password': null,
|
||||
'loading': false,
|
||||
'errors': {}
|
||||
});
|
||||
|
||||
const auth = useAuthStore();
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit = () => {
|
||||
const onSubmit = async () => {
|
||||
let loginSuccessful = false
|
||||
if (!data.username || !data.password) return;
|
||||
|
||||
data.loading = true;
|
||||
|
||||
axios.post('/login', {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
}).then(async (response) => {
|
||||
if (response.status === 200) {
|
||||
console.log(response);
|
||||
const timezone = await setTimezone(response.data);
|
||||
const auth = useAuthStore();
|
||||
const userStore = {
|
||||
user: response.data,
|
||||
password: data.password,
|
||||
timezone: timezone
|
||||
await axios.get('/sanctum/csrf-cookie').then(() => {
|
||||
axios.post('/login', {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
}).then(async (response) => {
|
||||
let timezone = await getTimezone();
|
||||
if(response.data.timezone) {
|
||||
timezone = response.data.timezone
|
||||
} else {
|
||||
response.data.timezone = timezone
|
||||
}
|
||||
auth.setUserData(userStore);
|
||||
Settings.defaultZone = timezone;
|
||||
router.push('/');
|
||||
} else {
|
||||
console.log(response)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
data.errors = error.response.data.errors
|
||||
data.loading = false;
|
||||
console.log(data)
|
||||
});
|
||||
|
||||
auth.loadUser(response.data);
|
||||
Settings.defaultZone = timezone;
|
||||
try {
|
||||
await router.push('/');
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
data.errors = error.response.data.errors
|
||||
data.loading = false;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const setTimezone = (user): Promise<string> => {
|
||||
const getTimezone = (): Promise<string> => {
|
||||
return axios.get("timezone").then((res) => {
|
||||
return res.data;
|
||||
}).catch(error => {
|
||||
console.log("Error: "+error);
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
@ -59,6 +60,13 @@ const setTimezone = (user): Promise<string> => {
|
|||
const required = (v) => {
|
||||
return !!v || $t('login.errors.required');
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
// TODO Create a route taht checks if the user is already logged in (laravel session cookie), if it's logged in
|
||||
// route.push /
|
||||
// In the BE make something like route.get('userIsAuthenticated')->return auth()->user()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,18 +1,63 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import {defineStore} from 'pinia'
|
||||
import type {User} from "@models/User.ts";
|
||||
import axios, {type AxiosResponse} from "axios";
|
||||
import {camelToSnake} from "@/helpers/AxiosHelper.ts";
|
||||
|
||||
export const useAuthStore = defineStore('authStore', () => {
|
||||
// A variable ref to store the user data
|
||||
const userData = ref(null)
|
||||
|
||||
// A function acts as a setter to set the incoming user data
|
||||
const setUserData = (newUserData: object) => {
|
||||
userData.value = newUserData
|
||||
export const baseUser = (): User => ({
|
||||
login: '',
|
||||
role: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
timezone: 'UTC',
|
||||
});
|
||||
|
||||
export const useAuthStore = defineStore('user', {
|
||||
state: () => ({
|
||||
userData: {} as User,
|
||||
}),
|
||||
actions: {
|
||||
loadUser(userData: User) {
|
||||
this.userData = {...userData};
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a specific field in the current user's state.
|
||||
* @param {object} payload - The payload containing the key and value to update.
|
||||
* @param {keyof (User)} payload.key - The key of the user property to update.
|
||||
* @param {any} payload.value - The new value for the property.
|
||||
*/
|
||||
updateField(payload: { key: keyof (User); value: any }) {
|
||||
this.userData[payload.key] = payload.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the current user state to its base values.
|
||||
*/
|
||||
resetUser() {
|
||||
this.userData = {...baseUser()};
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the current user on the server.
|
||||
* @returns {Promise<any>} A promise that resolves with the response data from the server.
|
||||
*/
|
||||
async updateUser() {
|
||||
if (!this.userData.id) {
|
||||
console.error("Error: User ID is missing. Cannot update.");
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = camelToSnake({...this.userData});
|
||||
|
||||
return await axios.put(`/user/${userData.id}`, userData)
|
||||
.then((response: AxiosResponse) => {
|
||||
return response.data;
|
||||
}).catch((error: Error) => {
|
||||
console.error("Error: " + error.message);
|
||||
|
||||
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
userData.value = null
|
||||
}
|
||||
|
||||
return { userData, setUserData, logout }
|
||||
})
|
||||
});
|
|
@ -67,6 +67,5 @@ export const useShowStore = defineStore('show', {
|
|||
console.error("Error: " + error.message);
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
})
|
|
@ -4,6 +4,7 @@ use App\Helpers\Preferences;
|
|||
use App\Http\Controllers\FileController;
|
||||
use App\Http\Controllers\PodcastController;
|
||||
use App\Http\Controllers\PodcastEpisodeController;
|
||||
use App\Http\Controllers\ScheduleController;
|
||||
use App\Http\Controllers\Show\ShowController;
|
||||
use App\Http\Controllers\Show\ShowDaysController;
|
||||
use App\Http\Controllers\ShowInstance\ShowInstancesController;
|
||||
|
@ -27,6 +28,7 @@ use Illuminate\Support\Facades\Route;
|
|||
*/
|
||||
|
||||
|
||||
Route::middleware('auth')->get('/user/profile', [UserController::class, 'userProfile']);
|
||||
|
||||
/**
|
||||
* Create routes without create method
|
||||
|
@ -47,6 +49,9 @@ Route::resources([
|
|||
'except' => 'create'
|
||||
]);
|
||||
|
||||
Route::middleware('auth:sanctum')->put('/user/{user}',[UserController::class,'update']);
|
||||
|
||||
Route::get('/schedule',[ScheduleController::class, 'index']);
|
||||
/**
|
||||
* Custom file route
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue