añadido crd para Song y transladada la logica de /gallery a un controller
This commit is contained in:
14
app/Http/Controllers/GalleryController.php
Normal file
14
app/Http/Controllers/GalleryController.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class GalleryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return Inertia::render('Gallery');
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,17 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
|
||||
class RootController extends Controller
|
||||
{
|
||||
// Muestra Login
|
||||
public function index()
|
||||
public function index(): Response
|
||||
{
|
||||
return Inertia::render('Auth/Login', [
|
||||
'canResetPassword' => false,
|
||||
'status' => session('status'),
|
||||
]); }
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,44 +2,80 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Song;
|
||||
use App\Policies\SongPolicy;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
class SongController extends Controller
|
||||
{ use AuthorizesRequests;
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
// render page
|
||||
Inertia::render('Songs/Index', [
|
||||
'songs' => Song::all()
|
||||
return Inertia::render('Songs/Index', [
|
||||
'songs' => Song::where('user_id', auth()->id())->paginate(10),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
// Store a new song
|
||||
Gate::authorize("create", Song::class);
|
||||
|
||||
$validated = $request->validate([
|
||||
'song' => 'max:108826630',
|
||||
]);
|
||||
$song = new Song();
|
||||
$song->user_id = auth()->id();
|
||||
|
||||
$file = $request->song;
|
||||
$path = $file->store('songs');
|
||||
$song->title = $file->getClientOriginalName();
|
||||
$song->path = $path;
|
||||
$song->artist = $file->getClientOriginalName();
|
||||
|
||||
$ret = $song->save();
|
||||
|
||||
return response()->json([
|
||||
'data' => $ret ? "Guardado " . $song->title : 'sin archivo'
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
/**
|
||||
* @return void
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function update(Request $request, $id):void
|
||||
{
|
||||
// Update specified song
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
// Delete specified song
|
||||
$this->authorize('delete', Song::class);
|
||||
|
||||
$song = Song::find($id);
|
||||
if ($song) {
|
||||
$song->delete();
|
||||
}
|
||||
}
|
||||
$user = User::find(auth()->id());
|
||||
|
||||
public function stream($id)
|
||||
//Gate::authorize('delete', $user , $song);
|
||||
$this->authorize('delete', $song);
|
||||
|
||||
if ($song) {
|
||||
// Delete the related file
|
||||
if (Storage::exists($song->path)) {
|
||||
Storage::delete($song->path);
|
||||
}
|
||||
Song::destroy($id);
|
||||
}
|
||||
return response()->noContent(200);
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function stream($id):void
|
||||
{
|
||||
// Stream specified song
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
class Song extends Model
|
||||
{
|
||||
//
|
||||
protected $fillable = ["title", 'artist', 'cover', 'user_id'];
|
||||
protected $fillable = ["title", 'artist', 'cover', 'user_id', 'path'];
|
||||
/**
|
||||
* @return BelongsTo<User,Song>
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ class SongPolicy
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
public function create(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class SongPolicy
|
||||
*/
|
||||
public function delete(User $user, Song $song): bool
|
||||
{
|
||||
return $song->user_id === $user->id;
|
||||
return $song->user_id == $user->id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,12 +2,13 @@ import { SVGAttributes } from 'react';
|
||||
|
||||
export default function ApplicationLogo(props: SVGAttributes<SVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 316 316"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z" />
|
||||
<svg {...props} viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M50 40 H250 V80 H100 V140 H220 V180 H100 V280 H50 V40"
|
||||
stroke="currentColor"
|
||||
strokeWidth="30"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
27
resources/js/Components/BotonAdd.tsx
Normal file
27
resources/js/Components/BotonAdd.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
export default function BotonAdd({
|
||||
show,
|
||||
setshow,
|
||||
}: {
|
||||
show: boolean;
|
||||
setshow: (arg0: boolean) => void;
|
||||
}) {
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="fixed bottom-0 right-0 m-4 flex h-[60px] w-[60px] items-center justify-center rounded-full bg-blue-600 text-white hover:bg-blue-700"
|
||||
onClick={() => setshow(true)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
className="bi bi-plus"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
11
resources/js/Components/Icons/AddIcon.tsx
Normal file
11
resources/js/Components/Icons/AddIcon.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function AddIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
height="1.5em"
|
||||
>
|
||||
<path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
12
resources/js/Components/Icons/AddStackIcon.tsx
Normal file
12
resources/js/Components/Icons/AddStackIcon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function AddStackIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1.5em"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path d="M 432 2 Q 414 4 412 22 L 412 58 L 412 58 L 376 58 L 376 58 Q 358 60 356 78 Q 358 96 376 98 L 412 98 L 412 98 L 412 134 L 412 134 Q 414 152 432 154 Q 450 152 452 134 L 452 98 L 452 98 L 488 98 L 488 98 Q 506 96 508 78 Q 506 60 488 58 L 452 58 L 452 58 L 452 22 L 452 22 Q 450 4 432 2 L 432 2 Z M 256 126 Q 243 126 231 131 L 22 221 L 22 221 Q 1 231 0 254 Q 1 277 22 287 L 231 377 L 231 377 Q 243 382 256 382 Q 269 382 281 377 L 490 287 L 490 287 Q 511 277 512 254 Q 511 231 490 221 L 281 131 L 281 131 Q 269 126 256 126 L 256 126 Z M 250 175 Q 253 174 256 174 Q 259 174 262 175 L 446 254 L 446 254 L 262 333 L 262 333 Q 259 334 256 334 Q 253 334 250 333 L 66 254 L 66 254 L 250 175 L 250 175 Z M 53 335 L 22 349 L 53 335 L 22 349 Q 1 359 0 382 Q 1 405 22 415 L 231 505 L 231 505 Q 243 510 256 510 Q 269 510 281 505 L 490 415 L 490 415 Q 511 405 512 382 Q 511 359 490 349 L 459 335 L 459 335 L 398 362 L 398 362 L 446 382 L 446 382 L 262 461 L 262 461 Q 259 462 256 462 Q 253 462 250 461 L 66 382 L 66 382 L 114 362 L 114 362 L 53 335 L 53 335 Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
15
resources/js/Components/Icons/PauseIcon.tsx
Normal file
15
resources/js/Components/Icons/PauseIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
const PauseIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM224 192l0 128c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-128c0-17.7 14.3-32 32-32s32 14.3 32 32zm128 0l0 128c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-128c0-17.7 14.3-32 32-32s32 14.3 32 32z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default PauseIcon;
|
||||
15
resources/js/Components/Icons/PlayIcon.tsx
Normal file
15
resources/js/Components/Icons/PlayIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
const PlayIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM188.3 147.1c-7.6 4.2-12.3 12.3-12.3 20.9l0 176c0 8.7 4.7 16.7 12.3 20.9s16.8 4.1 24.3-.5l144-88c7.1-4.4 11.5-12.1 11.5-20.5s-4.4-16.1-11.5-20.5l-144-88c-7.4-4.5-16.7-4.7-24.3-.5z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayIcon;
|
||||
52
resources/js/Components/ModalAñadirCancion.tsx
Normal file
52
resources/js/Components/ModalAñadirCancion.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Input } from '@headlessui/react';
|
||||
import { FormEvent, useState } from 'react';
|
||||
import InputLabel from './InputLabel';
|
||||
import Modal from './Modal';
|
||||
import PrimaryButton from './PrimaryButton';
|
||||
|
||||
export default function ModalAñadirCancion({
|
||||
showaddmodal,
|
||||
addcancion,
|
||||
setShowaddmodal,
|
||||
}: {
|
||||
showaddmodal: boolean;
|
||||
addcancion: (event: FormEvent<HTMLFormElement>) => void;
|
||||
setShowaddmodal: (arg0: boolean) => void;
|
||||
}) {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={showaddmodal}
|
||||
onClose={() => setShowaddmodal(false)}
|
||||
closeable={true}
|
||||
maxWidth="md"
|
||||
>
|
||||
<form onSubmit={addcancion} className="p-6">
|
||||
<h2 className="mb-2 text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
Añadiendo Cancion
|
||||
</h2>
|
||||
<hr />
|
||||
<div className="mt-2">
|
||||
<InputLabel>Cancion a añadir. máximo 50MB</InputLabel>
|
||||
<Input
|
||||
type="file"
|
||||
onChange={(e: any) => setSelectedFile(e.target.files[0])}
|
||||
name="song"
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm file:mr-4 file:rounded-md file:border-0 file:bg-indigo-100 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-indigo-700 hover:file:bg-indigo-100 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300"
|
||||
/>
|
||||
<hr className="mt-2" />
|
||||
<div className="flex place-content-center">
|
||||
<PrimaryButton
|
||||
className="mt-2"
|
||||
type="submit"
|
||||
disabled={!selectedFile}
|
||||
>
|
||||
Subir
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
46
resources/js/Components/ModalRemoveCancion.tsx
Normal file
46
resources/js/Components/ModalRemoveCancion.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Cancion } from '@/types/types';
|
||||
import { FormEvent, useState } from 'react';
|
||||
import Checkbox from './Checkbox';
|
||||
import InputLabel from './InputLabel';
|
||||
import Modal from './Modal';
|
||||
import PrimaryButton from './PrimaryButton';
|
||||
|
||||
export default function ModalRemoveCancion({
|
||||
show,
|
||||
cancion,
|
||||
remove,
|
||||
close,
|
||||
}: {
|
||||
show: boolean;
|
||||
cancion: Cancion;
|
||||
remove: (e: FormEvent) => void;
|
||||
close: (arg0: boolean) => void;
|
||||
}) {
|
||||
const [checked, setcheked] = useState(false);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={show}
|
||||
onClose={() => close(false)}
|
||||
closeable={true}
|
||||
maxWidth="md"
|
||||
>
|
||||
<form onSubmit={remove} className="p-6">
|
||||
<h2 className="mb-2 text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
Eliminando Canción
|
||||
</h2>
|
||||
<hr />
|
||||
<div className="mt-2 flex">
|
||||
<Checkbox onChange={() => setcheked(!checked)} />
|
||||
<InputLabel>Cancion a eliminar: {cancion.title}</InputLabel>
|
||||
<hr className="mt-2" />
|
||||
<div className="flex place-content-center">
|
||||
<PrimaryButton className="mt-2" type="submit" disabled={!checked}>
|
||||
Aceptar
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import PauseIcon from './Icons/PauseIcon';
|
||||
import PlayIcon from './Icons/PlayIcon';
|
||||
|
||||
export default function Reproductor() {
|
||||
export default function Reproductor({ titulo }) {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [timeLeft, setTimeLeft] = useState('0:00');
|
||||
@@ -34,28 +36,9 @@ export default function Reproductor() {
|
||||
className="mx-4 text-white"
|
||||
onClick={() => setIsPlaying(!isPlaying)}
|
||||
>
|
||||
{isPlaying ? (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<rect x="6" y="4" width="4" height="16" />
|
||||
<rect x="14" y="4" width="4" height="16" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
)}
|
||||
{!isPlaying ? <PlayIcon /> : <PauseIcon />}
|
||||
</button>
|
||||
|
||||
<span className="text-white">{titulo}</span>
|
||||
<span className="text-white">{timeLeft}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,12 @@ export default function Authenticated({
|
||||
>
|
||||
Gallery
|
||||
</NavLink>
|
||||
<NavLink
|
||||
href={route('canciones')}
|
||||
active={route().current('canciones')}
|
||||
>
|
||||
Canciones
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -137,6 +143,18 @@ export default function Authenticated({
|
||||
>
|
||||
Dashboard
|
||||
</ResponsiveNavLink>
|
||||
<ResponsiveNavLink
|
||||
href={route('gallery')}
|
||||
active={route().current('gallery')}
|
||||
>
|
||||
Gallery
|
||||
</ResponsiveNavLink>
|
||||
<ResponsiveNavLink
|
||||
href={route('canciones')}
|
||||
active={route().current('canciones')}
|
||||
>
|
||||
Canciones
|
||||
</ResponsiveNavLink>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pb-1 pt-4 dark:border-gray-600">
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import AddIcon from '@/Components/Icons/AddIcon';
|
||||
import AddStackIcon from '@/Components/Icons/AddStackIcon';
|
||||
import PrimaryButton from '@/Components/PrimaryButton';
|
||||
import Reproductor from '@/Components/Reproductor';
|
||||
import SecondaryButton from '@/Components/SecondaryButton';
|
||||
import Authenticated from '@/Layouts/AuthenticatedLayout';
|
||||
import { useState } from 'react';
|
||||
|
||||
@@ -8,7 +12,8 @@ export default function Gallery() {
|
||||
id: 1,
|
||||
title: 'Song 1',
|
||||
artist: 'Artist 1',
|
||||
coverArt: '/images/song1.jpg',
|
||||
coverArt:
|
||||
'https://duckduckgo.com/?q=Metallica%20American%20heavy%20metal%20band&ia=images&iax=images',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -26,7 +31,7 @@ export default function Gallery() {
|
||||
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
||||
const [cancionSeleccionada, setCancion] = useState({ title: '' });
|
||||
return (
|
||||
<Authenticated
|
||||
header={
|
||||
@@ -55,6 +60,14 @@ export default function Gallery() {
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
{song.artist}
|
||||
</p>
|
||||
<div className="mt-1 flex gap-1">
|
||||
<PrimaryButton>
|
||||
<AddIcon />
|
||||
</PrimaryButton>
|
||||
<SecondaryButton>
|
||||
<AddStackIcon />
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,13 +1,143 @@
|
||||
export default function Index({ songs }) {
|
||||
console.log('Songs/Index.jsx se está ejecutando');
|
||||
import BotonAdd from '@/Components/BotonAdd';
|
||||
import ModalAñadirCancion from '@/Components/ModalAñadirCancion';
|
||||
import ModalRemoveCancion from '@/Components/ModalRemoveCancion';
|
||||
import Authenticated from '@/Layouts/AuthenticatedLayout';
|
||||
import { Cancion } from '@/types/types';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import axios from 'axios';
|
||||
import { FormEvent, useEffect, useState } from 'react';
|
||||
|
||||
export default function Index({ songs }: { songs: Cancion[] }) {
|
||||
const [showaddmodal, setshowmodal] = useState(false);
|
||||
const [showRemove, setRemove] = useState(false);
|
||||
const [selcan, setcan] = useState<Cancion>({} as Cancion);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.ctrlKey && event.key === 'k') {
|
||||
event.preventDefault();
|
||||
setshowmodal(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyPress);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyPress);
|
||||
};
|
||||
}, []);
|
||||
|
||||
function addcancion(event: FormEvent<HTMLFormElement>): void {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(event.currentTarget);
|
||||
|
||||
axios
|
||||
.post('/canciones', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
.then((response: { data: string }) => {
|
||||
console.log('Song added successfully', response.data);
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(() => {
|
||||
setshowmodal(false);
|
||||
});
|
||||
}
|
||||
|
||||
function handleRemove(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
axios
|
||||
.delete(`/canciones/${selcan.id}`)
|
||||
.then(() => {
|
||||
setcan({} as Cancion);
|
||||
setRemove(false);
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error removing song:', error);
|
||||
setRemove(false);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Mis Canciones</h1>
|
||||
<ul>
|
||||
{songs.map((song) => (
|
||||
<li key={song.id}>{song.title}</li>
|
||||
<Authenticated
|
||||
header={
|
||||
<h2 className="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
Crud Canciones
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
<Head title="Canciones" />
|
||||
<div className="container">
|
||||
{songs.data.length === 0 ? (
|
||||
<p className="mt-4 w-full text-center text-xl text-gray-500 dark:text-gray-300">
|
||||
No hay canciones cargadas para tu usuario. Quiere Añadir una? Aprete{' '}
|
||||
<br className="mb-2" />
|
||||
<kbd className="rounded-md border bg-teal-950">Ctrl</kbd> +{' '}
|
||||
<kbd className="rounded-md border bg-teal-950">k</kbd>
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex justify-center overflow-x-auto">
|
||||
<table className="table-auto">
|
||||
<thead>
|
||||
<tr className="border-b bg-gray-50 text-center text-sm font-semibold uppercase tracking-wider text-gray-700 dark:bg-gray-700 dark:text-gray-400">
|
||||
<th className="px-6 py-3">Nombre</th>
|
||||
<th className="px-6 py-3">Artista</th>
|
||||
<th className="px-6 py-3">path</th>
|
||||
<th className="px-6 py-3">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-800">
|
||||
{songs.data.map((song: Cancion) => (
|
||||
<tr
|
||||
key={song.id}
|
||||
className="hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
>
|
||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-900 dark:text-gray-200">
|
||||
{song.title}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-900 dark:text-gray-200">
|
||||
{song.artist}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-900 dark:text-gray-200">
|
||||
{song.path}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-6 py-4 text-sm">
|
||||
<button className="mr-2 rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700">
|
||||
Editar
|
||||
</button>
|
||||
<button
|
||||
className="rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700"
|
||||
onClick={() => {
|
||||
setcan(song);
|
||||
setRemove(true);
|
||||
}}
|
||||
>
|
||||
Eliminar
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
<BotonAdd show={true} setshow={setshowmodal} />
|
||||
<ModalAñadirCancion
|
||||
showaddmodal={showaddmodal}
|
||||
addcancion={addcancion}
|
||||
setShowaddmodal={setshowmodal}
|
||||
/>
|
||||
<ModalRemoveCancion
|
||||
show={showRemove}
|
||||
close={setRemove}
|
||||
cancion={selcan}
|
||||
remove={handleRemove}
|
||||
/>
|
||||
</div>
|
||||
</Authenticated>
|
||||
);
|
||||
}
|
||||
|
||||
8
resources/js/types/types.d.ts
vendored
Normal file
8
resources/js/types/types.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export type Cancion = {
|
||||
name: ReactNode;
|
||||
id: number;
|
||||
title: string;
|
||||
artist: string | null;
|
||||
path: string;
|
||||
cover: string | null;
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\GalleryController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\RootController;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
use App\Http\Controllers\SongController;
|
||||
@@ -20,16 +20,16 @@ Route::middleware('auth')->group(function () {
|
||||
});
|
||||
|
||||
|
||||
Route::get('/gallery', function () {
|
||||
return Inertia::render('Gallery');
|
||||
})->middleware(['auth', 'verified'])->name('gallery');
|
||||
Route::get('/gallery', [GalleryController::class, "index"])
|
||||
->middleware(['auth', 'verified'])
|
||||
->name('gallery');
|
||||
|
||||
// Api canciones
|
||||
//
|
||||
Route::get('/songs', [SongController::class, 'index']);
|
||||
Route::get('/songs/stream/{id}', [SongController::class, 'stream']);
|
||||
Route::post('/songs', [SongController::class, 'store']);
|
||||
Route::put('/songs/{id}', [SongController::class, 'update']);
|
||||
Route::delete('/songs/{id}', [SongController::class, 'destroy']);
|
||||
Route::get('/canciones', [SongController::class, 'index'])->name('canciones');
|
||||
Route::get('/canciones/stream/{id}', [SongController::class, 'stream']);
|
||||
Route::post('/canciones', [SongController::class, 'store']);
|
||||
Route::put('/canciones/{id}', [SongController::class, 'update']);
|
||||
Route::delete('/canciones/{id}', [SongController::class, 'destroy'])->middleware(['auth', 'verified']);
|
||||
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
Reference in New Issue
Block a user