feat: Enhance UI with emoji page titles and improve tagging functionality

This commit updates the Layout component to include emojis in page titles for better visual cues. The Login component removes default credential placeholders for improved security. Additionally, the Search component is enhanced to allow immediate tagging of selected photos with existing tags, and it supports adding multiple tags at once, improving user experience. Documentation has been updated to reflect these changes.
This commit is contained in:
tanyar09
2025-12-09 13:01:35 -05:00
parent 0a109b198a
commit 6e196ff859
4 changed files with 67 additions and 32 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ export default function Layout() {
// Get page title based on route
const getPageTitle = () => {
const route = location.pathname
if (route === '/') return 'Home Page'
if (route === '/') return '🏠 Home Page'
if (route === '/scan') return '🗂️ Scan Photos'
if (route === '/process') return '⚙️ Process Faces'
if (route === '/search') return '🔍 Search Photos'
-6
View File
@@ -87,7 +87,6 @@ export default function Login() {
onChange={(e) => setUsername(e.target.value)}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="admin"
/>
</div>
@@ -106,7 +105,6 @@ export default function Login() {
onChange={(e) => setPassword(e.target.value)}
required
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="admin"
/>
<button
type="button"
@@ -127,10 +125,6 @@ export default function Login() {
{loading ? 'Logging in...' : 'Login'}
</button>
</form>
<div className="mt-6 text-center text-sm text-gray-500">
<p>Default credentials: admin / admin</p>
</div>
</div>
</div>
</div>
+60 -22
View File
@@ -519,37 +519,76 @@ export default function Search() {
return commonTags
}, [photoTags, selectedPhotos])
const handleSelectExistingTag = async (tagName: string) => {
if (selectedPhotos.size === 0 || !tagName.trim()) {
return
}
try {
// Add the selected tag to photos
await tagsApi.addToPhotos({
photo_ids: Array.from(selectedPhotos),
tag_names: [tagName.trim()],
})
// Clear the dropdown selection
setSelectedTagName('')
// Reload tags for selected photos so the new tag appears in the Tags area
await loadPhotoTags()
} catch (error) {
console.error('Error tagging photos:', error)
alert('Error tagging photos. Please try again.')
}
}
const handleAddTag = async () => {
if (selectedPhotos.size === 0) {
alert('Please select photos to tag.')
return
}
// Determine which tag to use: new tag input or selected from dropdown
const tagToAdd = newTagName.trim() || selectedTagName.trim()
// Collect both tags: selected existing tag and new tag name
const tagsToAdd: string[] = []
if (!tagToAdd) {
if (selectedTagName.trim()) {
tagsToAdd.push(selectedTagName.trim())
}
if (newTagName.trim()) {
tagsToAdd.push(newTagName.trim())
}
if (tagsToAdd.length === 0) {
alert('Please select a tag or enter a new tag name.')
return
}
try {
// If it's a new tag (from input field), create it first
if (newTagName.trim()) {
await tagsApi.create(newTagName.trim())
// Refresh available tags list
await loadTags()
// Clear the new tag input
setNewTagName('')
// Create any new tags first
const newTags = tagsToAdd.filter(tag =>
!availableTags.some(availableTag =>
availableTag.tag_name.toLowerCase() === tag.toLowerCase()
)
)
for (const newTag of newTags) {
await tagsApi.create(newTag)
}
// Add tag to photos
// Refresh available tags list if we created any
if (newTags.length > 0) {
await loadTags()
}
// Add all tags to photos in a single API call
await tagsApi.addToPhotos({
photo_ids: Array.from(selectedPhotos),
tag_names: [tagToAdd],
tag_names: tagsToAdd,
})
// Clear inputs after successful tagging
setSelectedTagName('')
setNewTagName('')
// Reload tags for selected photos
await loadPhotoTags()
} catch (error) {
@@ -1480,10 +1519,10 @@ export default function Search() {
<select
value={selectedTagName}
onChange={(e) => {
setSelectedTagName(e.target.value)
// Clear new tag input when selecting from dropdown
if (e.target.value) {
setNewTagName('')
const tagName = e.target.value
if (tagName) {
// Immediately add the tag and clear the dropdown
handleSelectExistingTag(tagName)
}
}}
className="w-full px-3 py-2 border border-gray-300 rounded"
@@ -1495,20 +1534,19 @@ export default function Search() {
</option>
))}
</select>
<p className="text-xs text-gray-500 mt-1">
Selecting a tag will immediately add it to the photos. You can select multiple tags in a row.
</p>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
Or Enter New Tag Name:
Enter New Tag Name:
</label>
<input
type="text"
value={newTagName}
onChange={(e) => {
setNewTagName(e.target.value)
// Clear selected tag when typing new tag
if (e.target.value) {
setSelectedTagName('')
}
}}
className="w-full px-3 py-2 border border-gray-300 rounded"
placeholder="Type new tag name..."
@@ -1519,7 +1557,7 @@ export default function Search() {
}}
/>
<p className="text-xs text-gray-500 mt-1">
New tags will be created in the database automatically.
You can select an existing tag and enter a new tag name to add both at once. New tags will be created in the database automatically.
</p>
</div>