diff --git a/.firebase/hosting.ZGlzdA.cache b/.firebase/hosting.ZGlzdA.cache new file mode 100644 index 0000000..6e816e1 --- /dev/null +++ b/.firebase/hosting.ZGlzdA.cache @@ -0,0 +1,11 @@ +index.html,1752878080470,9b8fdb94da9772e3fdc164ee7349299247c1dfda01634fd218d723f2224bf8c1 +assets/swipe-back-BPfbhfLI.js,1752878080470,e9d1df947b33957e85fc7bc25f337d7fcf4b9879fb28143ab647b02494cb407c +assets/status-tap-C1_bc3dv.js,1752878080470,46852d45f5582135c63f4a06b1d45843a02f9923dc3a82176595f77c11659cba +vite.svg,499162500000,699a02e0e68a579f687d364bbbe7633161244f35af068220aee37b1b33dfb3c7 +assets/md.transition-BWAHip3-.js,1752878080471,edab01bf56e19bf7f76626a7bcb126d46dbd7efde20e05ee7665e58f7138ae02 +assets/ios.transition-Bu_-K7p5.js,1752878080471,e1861e447c4450d0cce5b3a9a80f5094c2d3ed5217b75709d3721cea2a62f290 +assets/input-shims-D7jb4dA4.js,1752878080470,cedf2d57f4e43340fa0fdf87b33639681e89543af2bc5538cfa31d5deddb5d31 +assets/index7-Er27DGeg.js,1752878080470,1d42622024e40ddc65f4aa143043032c50a4db1bf5afbf932029a0f46cda7006 +assets/focus-visible-supuXXMI.js,1752878080471,df9266429356671847fa2c8123e1564bae645f75df3094f0055c365fa2beae28 +assets/index-B7vPvsG_.css,1752878080470,dbc57fb127dc9dde819d6ce6a258663f05013f29ec8905f282fe0087d44faaff +assets/index-CuxN5P9G.js,1752878080471,f901d381eb7e4cb488c572bcf2b3f16f891065275c33a8aecd8aca451a70f540 diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..b848417 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,8 @@ +{ + "projects": { + "webapp": "sings-a-lot", + "sings-a-lot-database": "firebase-herse" + }, + "targets": {}, + "etags": {} +} \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..13a2656 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,93 @@ +# Deployment Guide - Multi-Project Firebase Setup + +This project uses a multi-project Firebase setup where: +- **Web App**: Hosted on `sings-a-lot` project +- **Database & Functions**: Hosted on `firebase-herse` project + +## Project Structure + +``` +sings-a-lot (webapp) ← Frontend hosting +firebase-herse (database) ← Database, Functions, Rules +``` + +## Deployment Commands + +### Deploy Web App Only (to sings-a-lot) +```bash +npm run deploy:webapp +``` + +### Deploy Database & Functions Only (to firebase-herse) +```bash +npm run deploy:database +``` + +### Deploy Everything +```bash +npm run deploy:all +``` + +## Manual Deployment Steps + +### 1. Deploy Web App +```bash +# Build the app +npm run build + +# Switch to webapp project +firebase use webapp + +# Deploy hosting +firebase deploy --only hosting +``` + +### 2. Deploy Database & Functions +```bash +# Switch to database project +firebase use sings-a-lot-database + +# Deploy database rules and functions +firebase deploy --only database,functions +``` + +## Environment Configuration + +Make sure your `.env` file points to the correct database project: + +```env +# These should point to firebase-herse (database project) +VITE_FIREBASE_API_KEY=your-api-key +VITE_FIREBASE_AUTH_DOMAIN=firebase-herse.firebaseapp.com +VITE_FIREBASE_DATABASE_URL=https://firebase-herse-default-rtdb.firebaseio.com +VITE_FIREBASE_PROJECT_ID=firebase-herse +VITE_FIREBASE_STORAGE_BUCKET=firebase-herse.appspot.com +VITE_FIREBASE_MESSAGING_SENDER_ID=123456789 +VITE_FIREBASE_APP_ID=your-app-id + +# App Configuration +VITE_CONTROLLER_NAME=default +VITE_APP_TITLE=SingSalot AI +``` + +## Firebase Project Aliases + +- `webapp` → `sings-a-lot` (frontend hosting) +- `sings-a-lot-database` → `firebase-herse` (database, functions, rules) + +## Troubleshooting + +### If you get permission errors: +1. Make sure you're logged into Firebase CLI: `firebase login` +2. Check your project access: `firebase projects:list` +3. Verify project aliases: `firebase use` + +### If database connection fails: +1. Check that your `.env` file points to the correct database project +2. Verify database rules are deployed to the correct project +3. Check Firebase console for the correct project + +### If functions don't work: +1. Make sure functions are deployed to the database project +2. Check function logs: `firebase functions:log` +3. Verify function configuration in `functions/src/index.ts` \ No newline at end of file diff --git a/firebase.json b/firebase.json index 5735e4d..5494e11 100644 --- a/firebase.json +++ b/firebase.json @@ -7,5 +7,40 @@ }, "database": { "rules": "database.rules.json" + }, + "hosting": { + "public": "dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + "targets": { + "webapp": { + "hosting": { + "sings-a-lot": [ + "sings-a-lot" + ] + } + }, + "sings-a-lot-database": { + "functions": { + "functions": [ + "firebase-herse" + ] + }, + "database": { + "rules": [ + "firebase-herse" + ] + } + } } } \ No newline at end of file diff --git a/package.json b/package.json index 99d12b8..96eb5d9 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "deploy:webapp": "npm run build && firebase use webapp && firebase deploy --only hosting", + "deploy:database": "firebase use sings-a-lot-database && firebase deploy --only database,functions", + "deploy:all": "npm run build && firebase use webapp && firebase deploy --only hosting && firebase use sings-a-lot-database && firebase deploy --only database,functions" }, "dependencies": { "@ionic/core": "^8.6.5", diff --git a/src/components/common/SongItem.tsx b/src/components/common/SongItem.tsx index 5c6cd51..991c267 100644 --- a/src/components/common/SongItem.tsx +++ b/src/components/common/SongItem.tsx @@ -4,7 +4,7 @@ import { add, heart, heartOutline, trash } from 'ionicons/icons'; import ActionButton from './ActionButton'; import { useAppSelector } from '../../redux'; import { selectQueue, selectFavorites } from '../../redux'; -import type { SongItemProps } from '../../types'; +import type { SongItemProps, QueueItem, Song } from '../../types'; // Utility function to extract filename from path const extractFilename = (path: string): string => { @@ -31,8 +31,8 @@ const SongItem: React.FC = ({ const favorites = useAppSelector(selectFavorites); // Check if song is in queue or favorites based on path - const isInQueue = Object.values(queue).some(item => item.song.path === song.path); - const isInFavorites = Object.values(favorites).some(favSong => favSong.path === song.path); + const isInQueue = (Object.values(queue) as QueueItem[]).some(item => item.song.path === song.path); + const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path); const renderActionPanel = () => { const buttons = []; diff --git a/src/features/Queue/Queue.tsx b/src/features/Queue/Queue.tsx index 8120268..861d5b6 100644 --- a/src/features/Queue/Queue.tsx +++ b/src/features/Queue/Queue.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { IonButton, IonIcon, IonReorderGroup, IonReorder, IonItem, IonLabel, IonItemSliding, IonItemOptions, IonItemOption } from '@ionic/react'; -import { trash, reorderThreeOutline, reorderTwoOutline, playCircle } from 'ionicons/icons'; +import { trash, reorderThreeOutline, reorderTwoOutline } from 'ionicons/icons'; import { ActionButton } from '../../components/common'; import { useQueue } from '../../hooks'; import { useAppSelector } from '../../redux'; @@ -18,7 +18,6 @@ const Queue: React.FC = () => { const { queueItems, - queueStats, canReorder, handleRemoveFromQueue, } = useQueue(); diff --git a/src/hooks/useSongOperations.ts b/src/hooks/useSongOperations.ts index be2ecf9..c12d894 100644 --- a/src/hooks/useSongOperations.ts +++ b/src/hooks/useSongOperations.ts @@ -16,7 +16,7 @@ export const useSongOperations = () => { try { // Calculate the next order by finding the highest order value and adding 1 - const queueItems = Object.values(currentQueue); + const queueItems = Object.values(currentQueue) as QueueItem[]; const maxOrder = queueItems.length > 0 ? Math.max(...queueItems.map(item => item.order || 0)) : 0; diff --git a/src/redux/selectors.ts b/src/redux/selectors.ts index abe79ca..ef049c2 100644 --- a/src/redux/selectors.ts +++ b/src/redux/selectors.ts @@ -1,5 +1,5 @@ import { createSelector } from '@reduxjs/toolkit'; -import type { RootState, QueueItem, Singer, Song } from '../types'; +import type { RootState, QueueItem, Singer, Song, SongList } from '../types'; import { selectSongs, selectQueue, @@ -108,7 +108,7 @@ export const selectSingersArray = createSelector( export const selectSongListArray = createSelector( [selectSongList], - (songList) => objectToArray(songList) + (songList) => (objectToArray(songList) as SongList[]) ); export const selectArtistsArray = createSelector(