Skip to content

Commit

Permalink
Merge pull request #61 from Crudzaso/feature/profile-page
Browse files Browse the repository at this point in the history
add: add the profile page with the light mode
  • Loading branch information
DiegoAndresRamirez authored Nov 12, 2024
2 parents 534af02 + c8a553c commit fa0cc7d
Show file tree
Hide file tree
Showing 8 changed files with 680 additions and 78 deletions.
8 changes: 8 additions & 0 deletions Modules/Raffle/app/Http/Controllers/RaffleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,12 @@ public function getLastChanceRaffles()

return response()->json($raffles);
}


public function getActiveRaffles()
{
$activeRaffles = Raffle::where('end_date', '>', now())->get();
return response()->json(['active_raffles' => $activeRaffles]);
}

}
7 changes: 7 additions & 0 deletions Modules/Ticket/app/Http/Controllers/TicketController.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,11 @@ public function destroy($id)

return redirect()->route('tickets.index')->with('success', 'Ticket eliminado exitosamente.');
}

public function getUserTickets($userId)
{
$ticketsCount = Ticket::where('user_id', $userId)->count();
return response()->json(['tickets_count' => $ticketsCount]);
}

}
45 changes: 42 additions & 3 deletions app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;

class UserController extends Controller
Expand Down Expand Up @@ -71,19 +72,33 @@ public function edit(User $user)
*/
public function update(Request $request, User $user)
{
// Validar los datos del formulario
$validated = $request->validate([
'name' => 'required|string|max:255',
'lastname' => 'nullable|string|max:255',
'document' => 'nullable|string|unique:users,document,' . $user->id,
'document_type' => 'nullable|string',
'phone_number' => 'nullable|string',
'email' => 'required|email|unique:users,email,' . $user->id,
'profile_photo' => 'nullable|image|max:2048',
]);


// Manejar la foto de perfil
if ($request->hasFile('profile_photo')) {
// Usa el trait HasProfilePhoto para actualizar la foto de perfil
$user->updateProfilePhoto($request->file('profile_photo'));
}

// Actualizar otros datos del usuario
$user->update($validated);

return redirect()->route('users.index')->with('success', 'Usuario actualizado exitosamente.');

// Responder con el URL de la foto de perfil actualizado
return response()->json([
'message' => 'Usuario actualizado exitosamente.',
'profile_photo_url' => $user->profile_photo_url,
]);
}


/**
* Remove the specified resource from storage.
Expand All @@ -105,4 +120,28 @@ public function showProfile(User $user)
'user' => $user,
]);
}

public function updateProfilePhoto(Request $request, User $user)
{
// Validar que se está enviando una imagen
$request->validate([
'profile_photo' => 'required|image|max:2048',
]);

// Manejar la foto de perfil
if ($request->hasFile('profile_photo')) {
// Eliminar la foto anterior si existe
if ($user->profile_photo_path) {
Storage::disk('public')->delete($user->profile_photo_path);
}

// Actualizar la foto de perfil usando el trait HasProfilePhoto
$user->updateProfilePhoto($request->file('profile_photo'));
}

return response()->json([
'message' => 'Foto de perfil actualizada exitosamente.',
'profile_photo_url' => $user->profile_photo_url,
]);
}
}
6 changes: 2 additions & 4 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class User extends Authenticatable
'password',
'google_id',
'github_id',
'profile_photo_path',
];

protected $hidden = [
Expand All @@ -48,8 +49,5 @@ public function routeNotificationForWebhook()
return env('DISCORD_WEBHOOK_URL');
}

// Example of a one-to-many relationship
public function posts() {
return $this->hasMany(Post::class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('profile_photo_path')->nullable()->after('email');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};
123 changes: 68 additions & 55 deletions resources/js/Components/Profile/UserProfile.vue
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
<template>
<div :class="[isDarkMode ? 'bg-[#1a1a1c] text-white' : 'bg-[#F5F5F7] text-gray-900', 'min-h-screen p-6']">
<!-- Encabezado del Perfil -->
<div class="max-w-7xl mx-auto mb-8">
<div class="flex flex-col md:flex-row items-center justify-between">
<div class="flex items-center space-x-4 mb-6 md:mb-0">
<!-- Foto de Perfil con opción de carga -->
<div class="relative group cursor-pointer" @click="triggerFileInput">
<!-- Contenedor del Perfil -->
<div class="profile-container">
<!-- Encabezado del Perfil -->
<div class="profile-header">
<div class="flex flex-col items-center gap-4">
<!-- Foto de Perfil e Información del Usuario -->
<div class="profile-picture-container" @click="triggerFileInput">
<input type="file" ref="fileInput" class="hidden" @change="handleFileChange" accept="image/*" />
<div :class="[isDarkMode ? 'bg-blue-700' : 'bg-blue-600', 'w-24 h-24 rounded-full flex items-center justify-center overflow-hidden']">
<img v-if="profilePicture" :src="profilePicture" alt="Foto de Perfil" class="object-cover w-full h-full" />
<User v-else size="48" class="text-white" />
</div>
<div :class="[isDarkMode ? 'bg-black bg-opacity-50 text-white' : 'bg-black bg-opacity-50 text-white', 'absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity']">
<span>Cambiar Foto</span>
<div class="profile-picture">
<img v-if="profilePicture" :src="profilePicture" alt="Foto de Perfil" />
<User v-else size="60" class="text-white" />
</div>
</div>

<!-- Nombre y Fecha de Miembro -->
<div>
<h1 :class="[isDarkMode ? 'text-blue-400' : 'text-blue-600', 'text-3xl font-bold']">{{ user.name }}</h1>
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">Miembro desde {{ formatDate(user.created_at) }}</p>
<div class="user-details text-center">
<h1 class="user-name" style="color: white;">{{ user.name }}</h1>
<p class="user-location">📍 {{ user.location || 'SF, Bay Area' }}</p>
<p class="user-email">{{ user.email }}</p>
</div>
<div class="action-buttons flex gap-4">
<button class="follow-btn">Follow</button>
<button class="hire-btn">Hire Me</button>
</div>
</div>

<!-- Botón de regreso al Dashboard -->
<button :class="[isDarkMode ? 'bg-blue-700 hover:bg-blue-800' : 'bg-blue-600 hover:bg-blue-700', 'text-white px-4 py-2 rounded-lg']">
<a href="/dashboard">
<span style="color: white;">← Volver al Dashboard</span>
</a>
</button>
<!-- Estadísticas del Usuario -->
<div class="user-stats grid grid-cols-3 gap-4 mt-6">
<StatCard title="Earnings" :value="user.earnings" icon="DollarSign" />
<StatCard title="Projects" :value="user.projects" icon="Briefcase" />
<StatCard title="Success Rate" :value="user.successRate + '%'" icon="Percent" />
</div>
</div>
</div>

<!-- Contenido Principal -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="col-span-2">
<div class="main-content grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8">
<!-- Información del Usuario y Progreso -->
<div>
<PersonalInfo :user="user" />
<RewardProgress :comprados="user.ticketsComprados" :objetivo="user.ticketsObjetivo" class="mt-6" />
<ActivityChart :data="ticketData" class="mt-6" />
</div>

<!-- Tarjetas de Estadísticas -->
<div class="space-y-4">
<StatCard title="Tickets Comprados" :value="user.ticketsComprados" icon="Ticket" />
<StatCard title="Rifas Participadas" :value="user.rifasParticipadas" icon="Gift" />
Expand All @@ -53,22 +56,26 @@
<script setup>
import { ref, computed, defineProps } from 'vue';
import { useDarkMode } from '@/composables/useDarkMode';
import { useRouter } from 'vue-router';
import { User } from 'lucide-vue-next';
import StatCard from '@/Components/Profile/StatCard.vue';
import RewardProgress from '@/Components/Profile/RewardProgress.vue';
import ActivityChart from '@/Components/Profile/ActivityChart.vue';
import PersonalInfo from '@/Components/Profile/PersonalInfo.vue';
const { isDarkMode } = useDarkMode();
const router = useRouter();
const fileInput = ref(null);
const profilePicture = ref(null);
const props = defineProps({
user: {
type: Object,
default: () => ({
name: 'Usuario Desconocido',
created_at: 'N/A',
email: '',
location: '',
earnings: 0,
projects: 0,
successRate: 0,
ticketsComprados: 0,
ticketsObjetivo: 10,
rifasParticipadas: 0,
Expand All @@ -78,29 +85,17 @@ const props = defineProps({
},
});
const fileInput = ref(null);
const profilePicture = ref(null);
const ticketData = [
{ date: '2024-11-05', tickets: 2 },
{ date: '2024-11-06', tickets: 1 },
{ date: '2024-11-07', tickets: 3 },
{ date: '2024-11-08', tickets: 0 },
{ date: '2024-11-09', tickets: 2 },
{ date: '2024-11-10', tickets: 1 },
{ date: '2024-11-11', tickets: 2 },
];
const formatDate = (dateString) => {
if (!dateString) return 'Fecha desconocida';
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return new Date(dateString).toLocaleDateString('es-ES', options);
};
const triggerFileInput = () => {
if (fileInput.value) {
fileInput.value.click();
}
if (fileInput.value) fileInput.value.click();
};
const handleFileChange = (event) => {
Expand All @@ -113,34 +108,52 @@ const handleFileChange = (event) => {
reader.readAsDataURL(file);
}
};
const navigateToDashboard = () => {
router.push('/dashboard');
};
</script>

<style scoped>
.min-h-screen {
min-height: 100vh;
}
img {
border-radius: 50%;
.profile-container {
background-color: #1a1a1c;
color: #f5f5f7;
padding: 20px;
border-radius: 12px;
max-width: 900px;
margin: 0 auto;
}
.group:hover .opacity-100 {
opacity: 1;
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
}
.hover\:opacity-80:hover {
opacity: 0.8;
.profile-picture {
width: 80px;
height: 80px;
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background-color: #2d2d30;
}
.profile-picture {
transition: background-color 0.3s ease;
.action-buttons button {
padding: 10px 20px;
border-radius: 8px;
font-weight: bold;
}
.follow-btn {
background-color: #3a3a3d;
color: #f5f5f7;
}
.profile-overlay {
transition: opacity 0.3s ease;
.hire-btn {
background-color: #007bff;
color: #f5f5f7;
}
</style>
Loading

0 comments on commit fa0cc7d

Please sign in to comment.