fix(06.2): resolve four sharing UX issues found in re-test UAT

- AccountView: remove hardcoded @ prefix so handle matches what share dialog expects
- documents store: set is_shared=true optimistically after successful share so badge shows without refetch
- GET /api/documents/{id}: allow recipients of an active share to view the document (was returning 404 for non-owners)
- ShareModal: move Share button to its own full-width row so it no longer overflows the input area

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-01 19:32:51 +02:00
parent 52e54b859a
commit a0f6c2f663
4 changed files with 37 additions and 21 deletions
+12 -1
View File
@@ -542,9 +542,20 @@ async def get_document(
raise HTTPException(404, "Document not found")
doc = await session.get(Document, uid)
if doc is None or doc.user_id != current_user.id:
if doc is None:
raise HTTPException(404, "Document not found")
if doc.user_id != current_user.id:
# Allow recipients of an active share to view the document
share_result = await session.execute(
select(Share).where(
Share.document_id == uid,
Share.recipient_id == current_user.id,
)
)
if share_result.scalar_one_or_none() is None:
raise HTTPException(404, "Document not found")
meta = await storage.get_metadata(session, doc_id)
if meta is None:
raise HTTPException(404, "Document not found")
+20 -18
View File
@@ -28,28 +28,30 @@
</h2>
<!-- Handle input row -->
<div class="flex gap-2">
<input
v-model="handle"
type="text"
placeholder="Enter username handle"
class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
@keydown.enter="submitShare"
/>
<select
v-model="permission"
aria-label="Permission level"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-indigo-500 shrink-0"
>
<option value="view">Can view</option>
<option value="edit">Can edit</option>
</select>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<input
v-model="handle"
type="text"
placeholder="Enter username handle"
class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
@keydown.enter="submitShare"
/>
<select
v-model="permission"
aria-label="Permission level"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-indigo-500 shrink-0"
>
<option value="view">Can view</option>
<option value="edit">Can edit</option>
</select>
</div>
<button
@click="submitShare"
:disabled="submitting || !handle.trim()"
class="bg-indigo-600 hover:bg-indigo-700 text-white text-sm px-4 py-2 rounded-lg disabled:opacity-50 transition-colors shrink-0"
class="w-full bg-indigo-600 hover:bg-indigo-700 text-white text-sm px-4 py-2 rounded-lg disabled:opacity-50 transition-colors"
>
<span v-if="submitting" class="flex items-center gap-1.5">
<span v-if="submitting" class="flex items-center justify-center gap-1.5">
<span class="animate-spin rounded-full border-2 border-current border-t-transparent w-3 h-3"></span>
Sharing
</span>
+4 -1
View File
@@ -157,7 +157,10 @@ export const useDocumentsStore = defineStore('documents', () => {
}
async function shareDocument(docId, recipientHandle, permission = 'view') {
try { return await api.createShare(docId, recipientHandle, permission) } catch (e) { throw e }
const result = await api.createShare(docId, recipientHandle, permission)
const doc = documents.value.find(d => d.id === docId)
if (doc) doc.is_shared = true
return result
}
async function revokeShare(shareId) {
+1 -1
View File
@@ -9,7 +9,7 @@
<h3 class="font-semibold text-gray-800 mb-4">Account information</h3>
<div class="space-y-2 text-sm text-gray-700">
<div><span class="text-gray-500">Email:</span> {{ authStore.user?.email }}</div>
<div><span class="text-gray-500">Username:</span> @{{ authStore.user?.handle }}</div>
<div><span class="text-gray-500">Username:</span> {{ authStore.user?.handle }}</div>
<div class="flex items-center gap-2">
<span class="text-gray-500">Role:</span>
<span