Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-07-18 17:35:37 -05:00
parent 4b2d1dcf77
commit 278eacf493
9 changed files with 158 additions and 9 deletions

View File

@ -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

8
.firebaserc Normal file
View File

@ -0,0 +1,8 @@
{
"projects": {
"webapp": "sings-a-lot",
"sings-a-lot-database": "firebase-herse"
},
"targets": {},
"etags": {}
}

93
DEPLOYMENT.md Normal file
View File

@ -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`

View File

@ -7,5 +7,40 @@
}, },
"database": { "database": {
"rules": "database.rules.json" "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"
]
}
}
} }
} }

View File

@ -7,7 +7,10 @@
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "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": { "dependencies": {
"@ionic/core": "^8.6.5", "@ionic/core": "^8.6.5",

View File

@ -4,7 +4,7 @@ import { add, heart, heartOutline, trash } from 'ionicons/icons';
import ActionButton from './ActionButton'; import ActionButton from './ActionButton';
import { useAppSelector } from '../../redux'; import { useAppSelector } from '../../redux';
import { selectQueue, selectFavorites } 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 // Utility function to extract filename from path
const extractFilename = (path: string): string => { const extractFilename = (path: string): string => {
@ -31,8 +31,8 @@ const SongItem: React.FC<SongItemProps> = ({
const favorites = useAppSelector(selectFavorites); const favorites = useAppSelector(selectFavorites);
// Check if song is in queue or favorites based on path // Check if song is in queue or favorites based on path
const isInQueue = Object.values(queue).some(item => item.song.path === song.path); const isInQueue = (Object.values(queue) as QueueItem[]).some(item => item.song.path === song.path);
const isInFavorites = Object.values(favorites).some(favSong => favSong.path === song.path); const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path);
const renderActionPanel = () => { const renderActionPanel = () => {
const buttons = []; const buttons = [];

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { IonButton, IonIcon, IonReorderGroup, IonReorder, IonItem, IonLabel, IonItemSliding, IonItemOptions, IonItemOption } from '@ionic/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 { ActionButton } from '../../components/common';
import { useQueue } from '../../hooks'; import { useQueue } from '../../hooks';
import { useAppSelector } from '../../redux'; import { useAppSelector } from '../../redux';
@ -18,7 +18,6 @@ const Queue: React.FC = () => {
const { const {
queueItems, queueItems,
queueStats,
canReorder, canReorder,
handleRemoveFromQueue, handleRemoveFromQueue,
} = useQueue(); } = useQueue();

View File

@ -16,7 +16,7 @@ export const useSongOperations = () => {
try { try {
// Calculate the next order by finding the highest order value and adding 1 // 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 const maxOrder = queueItems.length > 0
? Math.max(...queueItems.map(item => item.order || 0)) ? Math.max(...queueItems.map(item => item.order || 0))
: 0; : 0;

View File

@ -1,5 +1,5 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import type { RootState, QueueItem, Singer, Song } from '../types'; import type { RootState, QueueItem, Singer, Song, SongList } from '../types';
import { import {
selectSongs, selectSongs,
selectQueue, selectQueue,
@ -108,7 +108,7 @@ export const selectSingersArray = createSelector(
export const selectSongListArray = createSelector( export const selectSongListArray = createSelector(
[selectSongList], [selectSongList],
(songList) => objectToArray(songList) (songList) => (objectToArray(songList) as SongList[])
); );
export const selectArtistsArray = createSelector( export const selectArtistsArray = createSelector(