ReviewOS

also looking at this

stacks/bench-review

Migrate :text bindings to {⁠{ }} interpolation (stx 0.2.72)

#67
Merged glennmichael123 wants to merge chore/migrate-text-to-mustache into main
52 files +344 -434
resources/components/Bench/Admin/AdminDashboard.stxmodified+2-2
Changes to resources/components/Bench/Admin/AdminDashboard.stx
@@ -38,12 +38,12 @@
3838 <div class="gap-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
3939 <StxLink to="/admin/reviews" class="block p-6 bg-white border border-gray-200 rounded-lg hover:shadow-sm transition-shadow">
4040 <p class="font-medium text-gray-500 text-sm">Pending reviews</p>
41 <p class="mt-2 font-bold text-3xl text-gray-900" :text="pendingCount()"></p>
41 <p class="mt-2 font-bold text-3xl text-gray-900">{{ pendingCount() }}</p>
4242 <p class="mt-2 font-semibold text-gray-600 text-sm">Open moderation queue &rarr;</p>
4343 </StxLink>
4444 <StxLink to="/admin/users" class="block p-6 bg-white border border-gray-200 rounded-lg hover:shadow-sm transition-shadow">
4545 <p class="font-medium text-gray-500 text-sm">Total users</p>
46 <p class="mt-2 font-bold text-3xl text-gray-900" :text="userCount()"></p>
46 <p class="mt-2 font-bold text-3xl text-gray-900">{{ userCount() }}</p>
4747 <p class="mt-2 font-semibold text-gray-600 text-sm">Manage users &rarr;</p>
4848 </StxLink>
4949 <div class="block p-6 bg-white border border-gray-200 rounded-lg">
resources/components/Bench/Admin/AdminShell.stxmodified+1-1
Changes to resources/components/Bench/Admin/AdminShell.stx
@@ -107,7 +107,7 @@
107107 </nav>
108108 </div>
109109 <div class="flex gap-x-4 items-center">
110 <span class="hidden sm:inline text-gray-600 text-sm" :text="adminName"></span>
110 <span class="hidden sm:inline text-gray-600 text-sm">{{ adminName }}</span>
111111 <StxLink to="/" class="text-gray-500 text-sm hover:text-gray-900">View site</StxLink>
112112 <button
113113 type="button"
resources/components/Bench/Admin/CredentialsTable.stxmodified+7-11
Changes to resources/components/Bench/Admin/CredentialsTable.stx
@@ -75,9 +75,7 @@
7575 <div
7676 role="alert"
7777 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
78 x-class="store.actionError() ? '' : 'hidden'"
79 :text="store.actionError()"
80 ></div>
78 x-class="store.actionError() ? '' : 'hidden'">{{ store.actionError() }}</div>
8179
8280 <div class="bg-white border border-gray-200 rounded-lg overflow-hidden">
8381 <div :if="store.loading() && store.claims().length === 0" class="px-4 py-12 text-center text-gray-500 text-sm">
@@ -100,23 +98,21 @@
10098 <div class="flex flex-wrap gap-2 items-center">
10199 <span
102100 class="inline-block px-2 py-0.5 font-medium text-xs rounded-full"
103 x-class="isJudgeClaim(claim) ? 'bg-amber-100 text-amber-800' : 'bg-gray-100 text-gray-700'"
104 :text="isJudgeClaim(claim) ? 'Judge claim' : 'Credential'"
105 ></span>
106 <span class="text-gray-500 text-xs" :text="formatDate(claim.credential_claimed_at)"></span>
101 x-class="isJudgeClaim(claim) ? 'bg-amber-100 text-amber-800' : 'bg-gray-100 text-gray-700'">{{ isJudgeClaim(claim) ? 'Judge claim' : 'Credential' }}</span>
102 <span class="text-gray-500 text-xs">{{ formatDate(claim.credential_claimed_at) }}</span>
107103 </div>
108 <h3 class="mt-1 font-semibold text-gray-900 text-base" :text="claimLabel(claim)"></h3>
104 <h3 class="mt-1 font-semibold text-gray-900 text-base">{{ claimLabel(claim) }}</h3>
109105 <p class="mt-0.5 text-gray-600 text-sm">
110 <span :text="claim.name"></span>
106 <span>{{ claim.name }}</span>
111107 <span class="text-gray-400"> · </span>
112 <span :text="claim.email"></span>
108 <span>{{ claim.email }}</span>
113109 </p>
114110 <!-- Prior rejection note, if this claim was rejected and resubmitted. -->
115111 <p
116112 class="mt-2 px-2 py-1 text-amber-800 text-xs bg-amber-50 border border-amber-200 rounded"
117113 x-class="hasNote(claim) ? '' : 'hidden'"
118114 >
119 Previously rejected: <span :text="claim.credential_rejection_note"></span>
115 Previously rejected: <span>{{ claim.credential_rejection_note }}</span>
120116 </p>
121117 </div>
122118 <div class="flex flex-shrink-0 gap-x-2 items-start">
resources/components/Bench/Admin/ModerationLog.stxmodified+6-8
Changes to resources/components/Bench/Admin/ModerationLog.stx
@@ -56,9 +56,7 @@
5656 <div
5757 role="alert"
5858 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
59 x-class="store.error() ? '' : 'hidden'"
60 :text="store.error()"
61 ></div>
59 x-class="store.error() ? '' : 'hidden'">{{ store.error() }}</div>
6260
6361 <div class="bg-white border border-gray-200 rounded-lg overflow-hidden">
6462 <div :if="store.loading() && store.logs().length === 0" class="px-4 py-12 text-center text-gray-500 text-sm">
@@ -76,14 +74,14 @@
7674 <ul x-class="hasLogs() ? 'divide-gray-100 divide-y' : 'hidden'">
7775 <li :for="entry in store.logs" class="px-4 py-3">
7876 <div class="flex flex-wrap gap-2 items-center">
79 <span class="inline-block px-2 py-0.5 font-medium text-xs rounded-full" x-class="actionTone(entry)" :text="actionLabel(entry)"></span>
80 <span class="font-medium text-gray-900 text-sm" :text="targetLabel(entry)"></span>
77 <span class="inline-block px-2 py-0.5 font-medium text-xs rounded-full" x-class="actionTone(entry)">{{ actionLabel(entry) }}</span>
78 <span class="font-medium text-gray-900 text-sm">{{ targetLabel(entry) }}</span>
8179 <span class="text-gray-400 text-xs">·</span>
82 <span class="text-gray-600 text-xs">by <span :text="entry.actor_name"></span></span>
80 <span class="text-gray-600 text-xs">by <span>{{ entry.actor_name }}</span></span>
8381 <span class="text-gray-400 text-xs">·</span>
84 <span class="text-gray-500 text-xs" :text="formatDate(entry.created_at)"></span>
82 <span class="text-gray-500 text-xs">{{ formatDate(entry.created_at) }}</span>
8583 </div>
86 <p class="mt-1 text-gray-600 text-xs" x-class="hasNote(entry) ? '' : 'hidden'" :text="entry.note"></p>
84 <p class="mt-1 text-gray-600 text-xs" x-class="hasNote(entry) ? '' : 'hidden'">{{ entry.note }}</p>
8785 </li>
8886 </ul>
8987 </div>
resources/components/Bench/Admin/ReviewsTable.stxmodified+14-22
Changes to resources/components/Bench/Admin/ReviewsTable.stx
@@ -156,7 +156,7 @@
156156 <div>
157157 <h1 class="font-bold text-2xl text-gray-900">Reviews</h1>
158158 <p class="mt-1 text-gray-600 text-sm">
159 <span :text="store.total()"></span> <span :text="store.statusFilter()"></span>
159 <span>{{ store.total() }}</span> <span>{{ store.statusFilter() }}</span>
160160 </p>
161161 </div>
162162 <div>
@@ -177,17 +177,13 @@
177177 type="button"
178178 @click="setFilter(tab.key)"
179179 class="px-4 py-2 font-medium text-sm border-b-2 transition-colors"
180 x-class="store.statusFilter() === tab.key ? 'border-gray-900 text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-900 hover:border-gray-300'"
181 :text="tab.label"
182 ></button>
180 x-class="store.statusFilter() === tab.key ? 'border-gray-900 text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-900 hover:border-gray-300'">{{ tab.label }}</button>
183181 </div>
184182
185183 <div
186184 role="alert"
187185 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
188 x-class="store.actionError() ? '' : 'hidden'"
189 :text="store.actionError()"
190 ></div>
186 x-class="store.actionError() ? '' : 'hidden'">{{ store.actionError() }}</div>
191187
192188 <div class="bg-white border border-gray-200 rounded-lg overflow-hidden">
193189 <div :if="store.loading() && store.reviews().length === 0" class="px-4 py-12 text-center text-gray-500 text-sm">
@@ -206,8 +202,8 @@
206202 <path d="M9 12h6m-6 4h6M7 4h10a2 2 0 012 2v12a2 2 0 01-2 2H7a2 2 0 01-2-2V6a2 2 0 012-2z" />
207203 </svg>
208204 </div>
209 <p class="font-semibold text-gray-900 text-sm" :text="emptyTitle()"></p>
210 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed" :text="emptyHint()"></p>
205 <p class="font-semibold text-gray-900 text-sm">{{ emptyTitle() }}</p>
206 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed">{{ emptyHint() }}</p>
211207 </div>
212208 <!-- x-class hidden (not :if) so the inner :for keeps its
213209 subscription across the loading -> loaded transition. The
@@ -220,19 +216,17 @@
220216 <div class="flex gap-x-2 items-center">
221217 <span
222218 class="inline-block px-2 py-0.5 font-medium text-xs rounded-full"
223 x-class="review.status === 'published' ? 'bg-emerald-100 text-emerald-800' : review.status === 'rejected' ? 'bg-red-100 text-red-800' : 'bg-amber-100 text-amber-800'"
224 :text="review.status"
225 ></span>
226 <span class="text-gray-500 text-xs" :text="formatDate(review.created_at)"></span>
219 x-class="review.status === 'published' ? 'bg-emerald-100 text-emerald-800' : review.status === 'rejected' ? 'bg-red-100 text-red-800' : 'bg-amber-100 text-amber-800'">{{ review.status }}</span>
220 <span class="text-gray-500 text-xs">{{ formatDate(review.created_at) }}</span>
227221 </div>
228 <h3 class="mt-1 font-semibold text-gray-900 text-base" :text="review.title"></h3>
222 <h3 class="mt-1 font-semibold text-gray-900 text-base">{{ review.title }}</h3>
229223 <p class="mt-1 text-gray-600 text-sm">
230 <span :text="review.judge.name"></span>
224 <span>{{ review.judge.name }}</span>
231225 <span class="text-gray-400"> · </span>
232 <span :if="review.user" :text="review.user ? review.user.email : ''"></span>
226 <span :if="review.user">{{ review.user ? review.user.email : '' }}</span>
233227 <span :if="!review.user" class="text-gray-400">(anonymous)</span>
234228 <span class="text-gray-400"> · </span>
235 <span :text="'★ ' + review.rating"></span>
229 <span>{{ '★ ' + review.rating }}</span>
236230 </p>
237231 </div>
238232 <div class="flex flex-shrink-0 flex-col gap-y-1 items-end">
@@ -258,9 +252,7 @@
258252 <button
259253 type="button"
260254 @click="toggleExpand(review)"
261 class="text-gray-500 text-xs hover:text-gray-900"
262 :text="expandedId() === review.id ? 'Hide content' : 'Show content'"
263 ></button>
255 class="text-gray-500 text-xs hover:text-gray-900">{{ expandedId() === review.id ? 'Hide content' : 'Show content' }}</button>
264256 </div>
265257 </div>
266258 <div class="mt-3 space-y-3" x-class="expandedId() === review.id ? '' : 'hidden'">
@@ -285,7 +277,7 @@
285277 class="inline-flex items-center justify-center px-3 py-1.5 font-semibold text-white text-xs bg-gray-900 hover:bg-gray-800 rounded disabled:opacity-50"
286278 >
287279 <svg :if="savingResponse()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
288 <span :text="savingResponse() ? 'Saving…' : 'Save response'"></span>
280 <span>{{ savingResponse() ? 'Saving…' : 'Save response' }}</span>
289281 </button>
290282 <button
291283 type="button"
@@ -302,7 +294,7 @@
302294
303295 <div class="flex justify-between items-center">
304296 <p class="text-gray-500 text-sm">
305 Page <span :text="store.page()"></span> of <span :text="store.totalPages()"></span>
297 Page <span>{{ store.page() }}</span> of <span>{{ store.totalPages() }}</span>
306298 </p>
307299 <div class="flex gap-x-2">
308300 <button
resources/components/Bench/Admin/UsersTable.stxmodified+13-19
Changes to resources/components/Bench/Admin/UsersTable.stx
@@ -127,7 +127,7 @@
127127 <div>
128128 <h1 class="font-bold text-2xl text-gray-900">Users</h1>
129129 <p class="mt-1 text-gray-600 text-sm">
130 <span :text="store.total()"></span> total
130 <span>{{ store.total() }}</span> total
131131 </p>
132132 </div>
133133 <div>
@@ -145,9 +145,7 @@
145145 <div
146146 role="alert"
147147 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
148 x-class="store.actionError() ? '' : 'hidden'"
149 :text="store.actionError()"
150 ></div>
148 x-class="store.actionError() ? '' : 'hidden'">{{ store.actionError() }}</div>
151149
152150 <div class="bg-white border border-gray-200 rounded-lg overflow-hidden">
153151 <table class="w-full divide-gray-200 divide-y">
@@ -175,20 +173,20 @@
175173 <path d="M17 20h5v-2a4 4 0 00-3-3.87M9 20H4v-2a4 4 0 013-3.87m6 5.87a4 4 0 100-8 4 4 0 000 8zm0 0a4 4 0 014 4M5 8a3 3 0 116 0 3 3 0 01-6 0zm10 0a3 3 0 116 0 3 3 0 01-6 0z" />
176174 </svg>
177175 </div>
178 <p class="font-semibold text-gray-900 text-sm" :text="emptyTitle()"></p>
179 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed" :text="emptyHint()"></p>
176 <p class="font-semibold text-gray-900 text-sm">{{ emptyTitle() }}</p>
177 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed">{{ emptyHint() }}</p>
180178 </td>
181179 </tr>
182180 <tr :for="user in store.users" class="hover:bg-gray-50">
183181 <td class="px-4 py-3 text-gray-900 text-sm">
184182 <div class="flex flex-col">
185 <span class="font-medium" :text="user.name || '(no name)'"></span>
186 <span class="text-gray-500 text-xs">id <span :text="user.id"></span></span>
183 <span class="font-medium">{{ user.name || '(no name)' }}</span>
184 <span class="text-gray-500 text-xs">id <span>{{ user.id }}</span></span>
187185 </div>
188186 </td>
189 <td class="px-4 py-3 text-gray-700 text-sm" :text="user.email"></td>
190 <td class="px-4 py-3 text-gray-700 text-sm" :text="rolesText(user)"></td>
191 <td class="px-4 py-3 text-gray-500 text-sm" :text="formatDate(user.created_at)"></td>
187 <td class="px-4 py-3 text-gray-700 text-sm">{{ user.email }}</td>
188 <td class="px-4 py-3 text-gray-700 text-sm">{{ rolesText(user) }}</td>
189 <td class="px-4 py-3 text-gray-500 text-sm">{{ formatDate(user.created_at) }}</td>
192190 <td class="px-4 py-3 text-right text-sm">
193191 <div class="flex justify-end gap-x-2">
194192 <button
@@ -201,9 +199,7 @@
201199 @click="togglePromote(user)"
202200 x-disabled="isSelf(user) && userHasAdmin(user)"
203201 class="font-medium text-sm hover:underline disabled:cursor-not-allowed disabled:opacity-40"
204 x-class="userHasAdmin(user) ? 'text-amber-700' : 'text-emerald-700'"
205 :text="userHasAdmin(user) ? 'Demote' : 'Promote'"
206 ></button>
202 x-class="userHasAdmin(user) ? 'text-amber-700' : 'text-emerald-700'">{{ userHasAdmin(user) ? 'Demote' : 'Promote' }}</button>
207203 <button
208204 type="button"
209205 @click="confirmDelete(user)"
@@ -219,7 +215,7 @@
219215
220216 <div class="flex justify-between items-center">
221217 <p class="text-gray-500 text-sm">
222 Page <span :text="store.page()"></span> of <span :text="store.totalPages()"></span>
218 Page <span>{{ store.page() }}</span> of <span>{{ store.totalPages() }}</span>
223219 </p>
224220 <div class="flex gap-x-2">
225221 <button
@@ -258,12 +254,10 @@
258254 <div
259255 role="alert"
260256 class="px-3 py-2 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
261 x-class="editError() ? '' : 'hidden'"
262 :text="editError()"
263 ></div>
257 x-class="editError() ? '' : 'hidden'">{{ editError() }}</div>
264258 <div class="flex justify-end gap-x-2">
265259 <button type="button" @click="cancelEdit()" x-disabled="saving" class="px-3 py-1.5 font-medium text-gray-700 text-sm bg-white border border-gray-300 rounded hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed">Cancel</button>
266 <button type="button" @click="saveEdit()" x-disabled="saving" class="inline-flex items-center justify-center px-3 py-1.5 font-medium text-white text-sm bg-gray-900 hover:bg-gray-800 rounded disabled:opacity-50 disabled:cursor-not-allowed"><svg :if="saving()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span :text="saving() ? 'Saving…' : 'Save'"></span></button>
260 <button type="button" @click="saveEdit()" x-disabled="saving" class="inline-flex items-center justify-center px-3 py-1.5 font-medium text-white text-sm bg-gray-900 hover:bg-gray-800 rounded disabled:opacity-50 disabled:cursor-not-allowed"><svg :if="saving()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span>{{ saving() ? 'Saving…' : 'Save' }}</span></button>
267261 </div>
268262 </div>
269263 </div>
resources/components/Bench/Article.stxmodified+3-3
Changes to resources/components/Bench/Article.stx
@@ -7,7 +7,7 @@
77 <div class="flex items-center">
88 <Icon :for="star in reviewStars" name="star" size="20" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-300'" />
99 </div>
10 <span class="ml-2 text-gray-500 text-sm" :text="`(${review.rating})`"></span>
10 <span class="ml-2 text-gray-500 text-sm">{{ `(${review.rating})` }}</span>
1111 </div>
1212
1313 <div class="relative review-content">
@@ -23,11 +23,11 @@
2323 <div class="flex items-center space-x-4">
2424 <button class="inline-flex items-center space-x-2 text-gray-600 hover:text-gray-900">
2525 <Icon name="heart" size="20" />
26 <span :text="review.likes"></span>
26 <span>{{ review.likes }}</span>
2727 </button>
2828 <button class="inline-flex items-center space-x-2 text-gray-600 hover:text-gray-900">
2929 <Icon name="message-square" size="20" />
30 <span :text="review.comments"></span>
30 <span>{{ review.comments }}</span>
3131 </button>
3232 </div>
3333 </div>
resources/components/Bench/Article/Comments.stxmodified+5-5
Changes to resources/components/Bench/Article/Comments.stx
@@ -56,7 +56,7 @@
5656<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl">
5757 <div class="pt-8 border-gray-200 border-t">
5858 <h2 class="font-semibold text-gray-900 text-xl">
59 Comments <span class="ml-2 text-base text-gray-500" x-text="`(${comments().length})`"></span>
59 Comments <span class="ml-2 text-base text-gray-500">{{ `(${comments().length})` }}</span>
6060 </h2>
6161
6262 <!-- Comment form -->
@@ -92,10 +92,10 @@
9292 <img class="flex-none h-10 w-10 rounded-full" x-src="comment.author.imageUrl" x-alt="comment.author.name">
9393 <div class="flex-auto">
9494 <div class="flex items-center justify-between">
95 <p class="font-semibold text-gray-900 text-sm" x-text="comment.author.name"></p>
96 <p class="text-gray-500 text-xs" x-text="comment.time"></p>
95 <p class="font-semibold text-gray-900 text-sm">{{ comment.author.name }}</p>
96 <p class="text-gray-500 text-xs">{{ comment.time }}</p>
9797 </div>
98 <p class="mt-1 leading-6 text-gray-700 text-sm" x-text="comment.content"></p>
98 <p class="mt-1 leading-6 text-gray-700 text-sm">{{ comment.content }}</p>
9999 <div class="flex gap-x-4 items-center mt-3 text-gray-500 text-xs">
100100 <button
101101 type="button"
@@ -103,7 +103,7 @@
103103 @click="likeComment(comment.id)"
104104 >
105105 <Icon name="bookmark" size="16" class="mr-1" />
106 <span x-text="comment.likes"></span>
106 <span>{{ comment.likes }}</span>
107107 </button>
108108 <button type="button" class="hover:text-gray-700">Reply</button>
109109 <button type="button" class="hover:text-gray-700">Report</button>
resources/components/Bench/ArticleEdit.stxmodified+2-4
Changes to resources/components/Bench/ArticleEdit.stx
@@ -258,9 +258,7 @@
258258 <div
259259 role="alert"
260260 class="px-3 py-2 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
261 x-class="editError() ? '' : 'hidden'"
262 :text="editError()"
263 ></div>
261 x-class="editError() ? '' : 'hidden'">{{ editError() }}</div>
264262
265263 <div class="flex justify-end gap-x-2 pt-2">
266264 <button
@@ -276,7 +274,7 @@
276274 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
277275 >
278276 <svg :if="editSaving()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
279 <span :text="editSaving() ? 'Saving…' : 'Save & resubmit'"></span>
277 <span>{{ editSaving() ? 'Saving…' : 'Save & resubmit' }}</span>
280278 </button>
281279 </div>
282280 </div>
resources/components/Bench/ArticleHeader.stxmodified+6-6
Changes to resources/components/Bench/ArticleHeader.stx
@@ -1,7 +1,7 @@
11<header class="relative">
22 <div class="mx-auto pb-8 pt-16 px-4 sm:px-6 lg:px-8 max-w-5xl">
33 <div class="text-center">
4 <h1 class="font-bold text-4xl text-gray-900 tracking-tight sm:text-5xl" :text="review.title"></h1>
4 <h1 class="font-bold text-4xl text-gray-900 tracking-tight sm:text-5xl">{{ review.title }}</h1>
55 </div>
66
77 <!-- Author and Judge info -->
@@ -10,23 +10,23 @@
1010 <img class="h-12 w-12 rounded-full" x-src="review.author.imageUrl" x-alt="review.author.name">
1111 <div class="ml-3">
1212 <p class="font-medium text-gray-900 text-sm">
13 <a href="#" class="hover:underline" :text="review.author.name"></a>
13 <a href="#" class="hover:underline">{{ review.author.name }}</a>
1414 </p>
1515 <div class="flex space-x-1 text-gray-500 text-sm">
16 <time :datetime="review.dateTime" :text="review.date"></time>
16 <time :datetime="review.dateTime">{{ review.date }}</time>
1717 <span aria-hidden="true">&middot;</span>
1818 </div>
1919 </div>
2020 </div>
2121 <div class="text-right">
2222 <div class="flex flex-col items-end">
23 <span class="inline-flex items-center mb-2 px-2 py-1 font-medium text-green-700 text-xs bg-green-50 ring-1 ring-green-600/20 ring-inset rounded-full" :text="review.type"></span>
23 <span class="inline-flex items-center mb-2 px-2 py-1 font-medium text-green-700 text-xs bg-green-50 ring-1 ring-green-600/20 ring-inset rounded-full">{{ review.type }}</span>
2424 <h2 class="font-semibold text-gray-900 text-lg">Reviewing</h2>
2525 <div class="flex items-center justify-end mt-1">
2626 <img class="h-10 w-10 rounded-full" x-src="review.judge.photo" x-alt="review.judge.name">
2727 <div class="ml-3">
28 <p class="font-medium text-gray-900 text-sm" :text="review.judge.name"></p>
29 <p class="text-gray-500 text-sm" :text="review.judge.court"></p>
28 <p class="font-medium text-gray-900 text-sm">{{ review.judge.name }}</p>
29 <p class="text-gray-500 text-sm">{{ review.judge.court }}</p>
3030 </div>
3131 </div>
3232 </div>
resources/components/Bench/ArticleView.stxmodified+26-40
Changes to resources/components/Bench/ArticleView.stx
@@ -479,8 +479,8 @@
479479 >
480480 <div class="flex flex-wrap items-start justify-between gap-3">
481481 <div class="flex-1 min-w-0">
482 <p class="font-semibold text-sm" :text="statusLabel"></p>
483 <p class="mt-1 text-xs leading-relaxed" :text="statusBlurb"></p>
482 <p class="font-semibold text-sm">{{ statusLabel }}</p>
483 <p class="mt-1 text-xs leading-relaxed">{{ statusBlurb }}</p>
484484 </div>
485485 <div class="flex flex-shrink-0 gap-x-2">
486486 <button
@@ -495,7 +495,7 @@
495495 class="inline-flex items-center justify-center px-3 py-1.5 font-semibold text-red-700 text-xs bg-white border border-red-200 rounded-md hover:bg-red-50 disabled:opacity-50"
496496 >
497497 <svg :if="deleting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
498 <span :text="deleting() ? 'Deleting…' : 'Delete'"></span>
498 <span>{{ deleting() ? 'Deleting…' : 'Delete' }}</span>
499499 </button>
500500 </div>
501501 </div>
@@ -507,15 +507,13 @@
507507 <div
508508 role="alert"
509509 class="mb-6 px-3 py-2 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
510 x-class="editError() ? '' : 'hidden'"
511 :text="editError()"
512 ></div>
510 x-class="editError() ? '' : 'hidden'">{{ editError() }}</div>
513511
514512 <!-- Topic chip -->
515 <p class="font-medium text-amber-600 text-center text-xs tracking-widest uppercase" :text="reviewTypeLabel"></p>
513 <p class="font-medium text-amber-600 text-center text-xs tracking-widest uppercase">{{ reviewTypeLabel }}</p>
516514
517515 <!-- Hero title — large serif, tight tracking, generous bottom margin -->
518 <h1 class="mt-4 font-serif font-bold leading-[1.1] text-4xl sm:text-5xl lg:text-6xl text-center text-gray-900 tracking-tight" :text="title"></h1>
516 <h1 class="mt-4 font-serif font-bold leading-[1.1] text-4xl sm:text-5xl lg:text-6xl text-center text-gray-900 tracking-tight">{{ title }}</h1>
519517
520518 <!-- Star rating + date + read time row. Centered, subtle. -->
521519 <div class="flex flex-wrap items-center justify-center gap-x-4 gap-y-2 mt-8 text-gray-500 text-sm">
@@ -529,12 +527,12 @@
529527 <path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.12 2.12 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16z" />
530528 </svg>
531529 </span>
532 <span class="ml-2 font-medium text-gray-700 tabular-nums" :text="`${rating}/5`"></span>
530 <span class="ml-2 font-medium text-gray-700 tabular-nums">{{ `${rating}/5` }}</span>
533531 </div>
534532 <span class="hidden sm:inline text-gray-300">·</span>
535 <span :text="date"></span>
533 <span>{{ date }}</span>
536534 <span class="hidden sm:inline text-gray-300">·</span>
537 <span :text="`${readMinutes} min read`"></span>
535 <span>{{ `${readMinutes} min read` }}</span>
538536 <!-- Reviewer line. Three rendering modes (bench-review#29, #36):
539537 - Linked: real user → "by <Name>" with click-through to /user/:id
540538 - Plain: anonymized → "by Anonymous attorney" with no link
@@ -542,9 +540,9 @@
542540 <span :if="hasReviewerName" class="hidden sm:inline text-gray-300">·</span>
543541 <span :if="hasReviewerLink" class="text-gray-500">
544542 by
545 <StxLink x-to="`/user/${reviewerId}`" class="ml-1 text-gray-700 hover:underline underline-offset-2" :text="reviewerName"></StxLink>
543 <StxLink x-to="`/user/${reviewerId}`" class="ml-1 text-gray-700 hover:underline underline-offset-2">{{ reviewerName }}</StxLink>
546544 </span>
547 <span :if="hasReviewerName && !hasReviewerLink()" class="text-gray-500" :text="`by ${reviewerName()}`"></span>
545 <span :if="hasReviewerName && !hasReviewerLink()" class="text-gray-500">{{ `by ${reviewerName()}` }}</span>
548546 </div>
549547
550548 <!-- Judge byline. Medium-style author row: round avatar + name + court.
@@ -560,16 +558,14 @@
560558 />
561559 <div
562560 :if="!judgeImage"
563 class="flex items-center justify-center h-12 w-12 font-semibold text-amber-700 text-sm bg-amber-50 ring-1 ring-amber-200/60 rounded-full"
564 :text="judgeInitials"
565 ></div>
561 class="flex items-center justify-center h-12 w-12 font-semibold text-amber-700 text-sm bg-amber-50 ring-1 ring-amber-200/60 rounded-full">{{ judgeInitials }}</div>
566562 <div class="min-w-0 flex-1">
567563 <p class="font-semibold text-gray-900 text-sm">
568564 <span class="text-gray-400 font-normal">Review of</span>
569 <StxLink :if="judgeId" x-to="`/judges/${judgeId}/profile`" class="ml-1 text-gray-900 hover:underline underline-offset-2" :text="judgeName"></StxLink>
570 <span :if="!judgeId" :text="judgeName"></span>
565 <StxLink :if="judgeId" x-to="`/judges/${judgeId}/profile`" class="ml-1 text-gray-900 hover:underline underline-offset-2">{{ judgeName }}</StxLink>
566 <span :if="!judgeId">{{ judgeName }}</span>
571567 </p>
572 <p :if="judgeCourt" class="text-gray-500 text-xs mt-0.5" :text="judgeCourt"></p>
568 <p :if="judgeCourt" class="text-gray-500 text-xs mt-0.5">{{ judgeCourt }}</p>
573569 </div>
574570 <StxLink
575571 :if="judgeId"
@@ -619,7 +615,7 @@
619615 x-class="hasJudgeResponse() ? '' : 'hidden'"
620616 >
621617 <p class="font-semibold text-amber-800 text-xs tracking-widest uppercase">Official response</p>
622 <p class="mt-1 font-semibold text-gray-900 text-sm" :text="`From ${judgeResponseName()}`"></p>
618 <p class="mt-1 font-semibold text-gray-900 text-sm">{{ `From ${judgeResponseName()}` }}</p>
623619 <div class="article-body mt-3 text-base text-gray-800" x-html="judgeResponseBody"></div>
624620 </div>
625621
@@ -643,10 +639,8 @@
643639 type="button"
644640 @click="postJudgeResponse()"
645641 x-disabled="judgeResponding"
646 class="px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
647 :text="judgeResponding() ? 'Posting…' : 'Post response'"
648 ></button>
649 <span class="text-red-700 text-xs" x-class="judgeRespondError() ? '' : 'hidden'" :text="judgeRespondError()"></span>
642 class="px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50">{{ judgeResponding() ? 'Posting…' : 'Post response' }}</button>
643 <span class="text-red-700 text-xs" x-class="judgeRespondError() ? '' : 'hidden'">{{ judgeRespondError() }}</span>
650644 </div>
651645 </div>
652646
@@ -679,7 +673,7 @@
679673 <!-- Caption shifts based on count + own-like state so the
680674 button reads naturally at 0, 1, and N — and credits the
681675 reader directly when they're the one who reacted. -->
682 <span class="text-xs text-current/80" :text="liked ? (likes === 1 ? 'You find this helpful' : `You and ${likes - 1} ${likes === 2 ? 'other' : 'others'} find this helpful`) : (likes === 0 ? 'Be the first to find this helpful' : likes === 1 ? 'One person finds this helpful' : `${likes} people find this helpful`)"></span>
676 <span class="text-xs text-current/80">{{ liked ? (likes === 1 ? 'You find this helpful' : `You and ${likes - 1} ${likes === 2 ? 'other' : 'others'} find this helpful`) : (likes === 0 ? 'Be the first to find this helpful' : likes === 1 ? 'One person finds this helpful' : `${likes} people find this helpful`) }}</span>
683677 </button>
684678 <!-- Author view: read-only badge showing how readers reacted.
685679 Same icon + count + caption shape, no click handler, no
@@ -694,8 +688,8 @@
694688 <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
695689 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
696690 </svg>
697 <span class="tabular-nums" :text="likes"></span>
698 <span class="text-xs text-gray-500" :text="likes === 0 ? 'No reactions yet' : likes === 1 ? '1 reader found this helpful' : `${likes} readers found this helpful`"></span>
691 <span class="tabular-nums">{{ likes }}</span>
692 <span class="text-xs text-gray-500">{{ likes === 0 ? 'No reactions yet' : likes === 1 ? '1 reader found this helpful' : `${likes} readers found this helpful` }}</span>
699693 </div>
700694 </div>
701695 <!-- Inline sign-in nudge (anonymous click). Soft prompt, no
@@ -714,9 +708,7 @@
714708 <div
715709 :if="likeError"
716710 role="alert"
717 class="px-3 py-1.5 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md"
718 :text="likeError"
719 ></div>
711 class="px-3 py-1.5 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md">{{ likeError }}</div>
720712 </div>
721713
722714 <div class="flex items-center gap-4 text-gray-500 text-sm">
@@ -726,9 +718,7 @@
726718 the author check. -->
727719 <span
728720 class="hidden sm:inline-flex items-center px-2.5 py-1 font-medium text-xs ring-1 rounded-full capitalize"
729 x-class="isMine() ? statusBadgeClasses() : 'hidden'"
730 :text="statusLabel"
731 ></span>
721 x-class="isMine() ? statusBadgeClasses() : 'hidden'">{{ statusLabel }}</span>
732722 <StxLink x-to="`/reviews`" class="text-gray-600 hover:text-gray-900 hover:underline underline-offset-2">
733723 ← All reviews
734724 </StxLink>
@@ -762,7 +752,7 @@
762752 <div class="mt-2 space-y-1.5">
763753 <label :for="opt in flagReasonOptions" class="flex items-center gap-2 px-3 py-2 text-gray-700 text-sm bg-white border border-gray-200 hover:bg-gray-50 rounded-md cursor-pointer transition-colors">
764754 <input type="radio" name="flag_reason" x-model="flagReason" :value="opt.value">
765 <span :text="opt.label"></span>
755 <span>{{ opt.label }}</span>
766756 </label>
767757 </div>
768758 </div>
@@ -781,9 +771,7 @@
781771 <div
782772 role="alert"
783773 class="mt-3 px-3 py-2 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md"
784 x-class="flagError() ? '' : 'hidden'"
785 :text="flagError()"
786 ></div>
774 x-class="flagError() ? '' : 'hidden'">{{ flagError() }}</div>
787775
788776 <div
789777 role="status"
@@ -801,9 +789,7 @@
801789 type="button"
802790 @click="submitFlag()"
803791 x-disabled="flagSubmitting"
804 class="px-3 py-1.5 font-medium text-white text-sm bg-red-600 hover:bg-red-700 rounded-md disabled:opacity-50"
805 :text="flagSubmitting() ? 'Sending…' : 'Send report'"
806 ></button>
792 class="px-3 py-1.5 font-medium text-white text-sm bg-red-600 hover:bg-red-700 rounded-md disabled:opacity-50">{{ flagSubmitting() ? 'Sending…' : 'Send report' }}</button>
807793 </div>
808794 </div>
809795 </div>
resources/components/Bench/BenchComingSoon.stxmodified+5-7
Changes to resources/components/Bench/BenchComingSoon.stx
@@ -109,21 +109,19 @@
109109 </div>
110110 <button type="submit" x-disabled="loading"
111111 class="flex-none px-3.5 py-2.5 font-semibold text-sm text-white bg-gray-600 hover:bg-gray-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 shadow-sm"
112 :text="loading() ? 'Subscribing...' : 'Notify me'">
113 Notify me
114 </button>
112 >{{ loading() ? 'Subscribing...' : 'Notify me' }}</button>
115113 </form>
116114 </div>
117115 <div :if="successVisible()" @close="successVisible.set(false)">
118116 <Notification type="success" position="top-right" :show="true" :duration="0">
119 <p class="text-sm font-medium" :text="toastTitle()"></p>
120 <p class="mt-1 text-sm opacity-90" :text="toastMessage()"></p>
117 <p class="text-sm font-medium">{{ toastTitle() }}</p>
118 <p class="mt-1 text-sm opacity-90">{{ toastMessage() }}</p>
121119 </Notification>
122120 </div>
123121 <div :if="errorVisible()" @close="errorVisible.set(false)">
124122 <Notification type="error" position="top-right" :show="true" :duration="0">
125 <p class="text-sm font-medium" :text="toastTitle()"></p>
126 <p class="mt-1 text-sm opacity-90" :text="toastMessage()"></p>
123 <p class="text-sm font-medium">{{ toastTitle() }}</p>
124 <p class="mt-1 text-sm opacity-90">{{ toastMessage() }}</p>
127125 </Notification>
128126 </div>
129127 </div>
resources/components/Bench/BenchHeader.stxmodified+12-16
Changes to resources/components/Bench/BenchHeader.stx
@@ -335,8 +335,8 @@
335335 >
336336 <img class="h-8 w-8 rounded-full object-cover" x-src="judge.photo" x-alt="judge.name">
337337 <div class="flex-1 min-w-0">
338 <p class="font-medium text-gray-900 text-sm truncate" :text="judge.name"></p>
339 <p class="text-gray-500 text-xs truncate" :text="(judge.court && judge.court.name) ? judge.court.name : (judge.location || '')"></p>
338 <p class="font-medium text-gray-900 text-sm truncate">{{ judge.name }}</p>
339 <p class="text-gray-500 text-xs truncate">{{ (judge.court && judge.court.name) ? judge.court.name : (judge.location || '') }}</p>
340340 </div>
341341 </button>
342342 </div>
@@ -352,8 +352,8 @@
352352 <svg class="h-4 w-4 text-gray-500" viewBox="0 0 20 20" fill="currentColor"><path d="M10.394 2.08a1 1 0 00-.788 0l-7 3A1 1 0 003 7v11a1 1 0 001 1h12a1 1 0 001-1V7a1 1 0 00-.606-.92l-7-3zM5 9h2v6H5V9zm6 0h2v6h-2V9z"/></svg>
353353 </div>
354354 <div class="flex-1 min-w-0">
355 <p class="font-medium text-gray-900 text-sm truncate" :text="court.name"></p>
356 <p class="text-gray-500 text-xs truncate" :text="(court.city || '') + (court.state ? ', ' + court.state : '')"></p>
355 <p class="font-medium text-gray-900 text-sm truncate">{{ court.name }}</p>
356 <p class="text-gray-500 text-xs truncate">{{ (court.city || '') + (court.state ? ', ' + court.state : '') }}</p>
357357 </div>
358358 </button>
359359 </div>
@@ -408,9 +408,7 @@
408408 would render even with zero unread. -->
409409 <span
410410 x-if="notifications.unreadCount()"
411 class="absolute -top-0.5 -right-0.5 inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 font-semibold text-[10px] text-white bg-red-500 rounded-full ring-2 ring-white"
412 :text="notifications.unreadCount() > 99 ? '99+' : notifications.unreadCount()"
413 ></span>
411 class="absolute -top-0.5 -right-0.5 inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 font-semibold text-[10px] text-white bg-red-500 rounded-full ring-2 ring-white">{{ notifications.unreadCount() > 99 ? '99+' : notifications.unreadCount() }}</span>
414412 </button>
415413
416414 <!-- Dropdown panel. Same x-class-hidden treatment as the
@@ -449,8 +447,8 @@
449447 unread; keeps the row scannable. -->
450448 <span class="mt-1 inline-flex h-2 w-2 flex-shrink-0 rounded-full" x-class="n.unread ? 'bg-blue-500' : 'bg-transparent'"></span>
451449 <div class="flex-1 min-w-0">
452 <p class="text-gray-900 text-sm" :text="notificationLabel(n)"></p>
453 <p class="mt-0.5 text-gray-400 text-xs" :text="relativeTime(n.created_at)"></p>
450 <p class="text-gray-900 text-sm">{{ notificationLabel(n) }}</p>
451 <p class="mt-0.5 text-gray-400 text-xs">{{ relativeTime(n.created_at) }}</p>
454452 </div>
455453 </button>
456454 </li>
@@ -476,10 +474,8 @@
476474 x-aria-expanded="userMenuOpen() ? 'true' : 'false'"
477475 >
478476 <span
479 class="inline-flex items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full"
480 :text="userInitials"
481 ></span>
482 <span class="hidden sm:inline font-semibold text-gray-900 text-sm/6 max-w-[10rem] truncate" :text="userName"></span>
477 class="inline-flex items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full">{{ userInitials }}</span>
478 <span class="hidden sm:inline font-semibold text-gray-900 text-sm/6 max-w-[10rem] truncate">{{ userName }}</span>
483479 <svg
484480 class="hidden sm:block text-gray-400 transition-transform"
485481 x-class="userMenuOpen() ? 'rotate-180' : ''"
@@ -498,10 +494,10 @@
498494 toggling between accounts don't fire an action against
499495 the wrong session. -->
500496 <div class="flex gap-3 items-center px-4 py-3 border-b border-gray-100">
501 <span class="inline-flex items-center justify-center h-9 w-9 flex-shrink-0 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full" :text="userInitials"></span>
497 <span class="inline-flex items-center justify-center h-9 w-9 flex-shrink-0 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full">{{ userInitials }}</span>
502498 <div class="flex-1 min-w-0">
503 <p class="font-semibold text-gray-900 text-sm truncate" :text="userName"></p>
504 <p class="text-gray-500 text-xs truncate" :text="userEmail"></p>
499 <p class="font-semibold text-gray-900 text-sm truncate">{{ userName }}</p>
500 <p class="text-gray-500 text-xs truncate">{{ userEmail }}</p>
505501 </div>
506502 </div>
507503
resources/components/Bench/Blog/BlogHeader.stxmodified+4-4
Changes to resources/components/Bench/Blog/BlogHeader.stx
@@ -9,7 +9,7 @@
99<header class="relative">
1010 <div class="mx-auto pb-8 pt-16 px-4 sm:px-6 lg:px-8 max-w-5xl">
1111 <div class="text-center">
12 <h1 class="font-bold text-4xl text-gray-900 tracking-tight sm:text-5xl" :text="post.title"></h1>
12 <h1 class="font-bold text-4xl text-gray-900 tracking-tight sm:text-5xl">{{ post.title }}</h1>
1313 </div>
1414
1515 <!-- Author and meta info -->
@@ -19,16 +19,16 @@
1919 <img class="h-12 w-12 rounded-full" x-src="post.author.imageUrl" x-alt="post.author.name">
2020 <div class="ml-3">
2121 <p class="font-medium text-gray-900 text-sm">
22 <a href="#" class="hover:underline" :text="post.author.name"></a>
22 <a href="#" class="hover:underline">{{ post.author.name }}</a>
2323 </p>
2424 </div>
2525 </div>
2626
2727 <!-- Reading time and date -->
2828 <div class="flex items-center space-x-4 text-gray-500 text-sm">
29 <span :text="readingTime"></span>
29 <span>{{ readingTime }}</span>
3030 <span aria-hidden="true">&middot;</span>
31 <time :datetime="post.dateTime" :text="post.date"></time>
31 <time :datetime="post.dateTime">{{ post.date }}</time>
3232 </div>
3333 </div>
3434 </div>
resources/components/Bench/CategoriesList.stxmodified+2-2
Changes to resources/components/Bench/CategoriesList.stx
@@ -68,8 +68,8 @@
6868 class="flex items-center px-3 py-2 font-medium text-sm rounded-md group"
6969 x-class="activeCategory === category.key ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'"
7070 >
71 <span :text="category.label"></span>
72 <span class="inline-block ml-auto px-2 py-0.5 font-medium text-gray-600 text-xs bg-gray-100 rounded-full" :text="category.count"></span>
71 <span>{{ category.label }}</span>
72 <span class="inline-block ml-auto px-2 py-0.5 font-medium text-gray-600 text-xs bg-gray-100 rounded-full">{{ category.count }}</span>
7373 </a>
7474 </nav>
7575</div>
resources/components/Bench/Comments.stxmodified+8-10
Changes to resources/components/Bench/Comments.stx
@@ -174,7 +174,7 @@
174174
175175<section class="mt-12 pt-8 border-t border-gray-200">
176176 <header class="flex items-end justify-between">
177 <h2 class="font-semibold text-gray-900 text-base" :text="`Comments (${comments().length})`"></h2>
177 <h2 class="font-semibold text-gray-900 text-base">{{ `Comments (${comments().length})` }}</h2>
178178 </header>
179179
180180 <!-- Composer slot — one if/else-if/else chain (mutually exclusive,
@@ -199,9 +199,7 @@
199199 <div
200200 role="alert"
201201 class="mt-2 px-3 py-2 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md"
202 x-class="commentError() ? '' : 'hidden'"
203 :text="commentError()"
204 ></div>
202 x-class="commentError() ? '' : 'hidden'">{{ commentError() }}</div>
205203
206204 <div class="flex flex-wrap items-center justify-between gap-2 mt-3">
207205 <label class="flex gap-2 items-center text-gray-600 text-xs cursor-pointer">
@@ -215,7 +213,7 @@
215213 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
216214 >
217215 <svg :if="commentSubmitting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
218 <span :text="commentSubmitting() ? 'Posting…' : 'Post comment'"></span>
216 <span>{{ commentSubmitting() ? 'Posting…' : 'Post comment' }}</span>
219217 </button>
220218 </div>
221219 </div>
@@ -257,15 +255,15 @@
257255 <ul :else class="space-y-4 mt-6">
258256 <li :for="c in comments" class="p-4 bg-white border border-gray-100 rounded-lg">
259257 <div class="flex items-start gap-3">
260 <span class="inline-flex flex-none items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full" :text="initialFor(c.author && c.author.name)"></span>
258 <span class="inline-flex flex-none items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full">{{ initialFor(c.author && c.author.name) }}</span>
261259 <div class="flex-1 min-w-0">
262260 <div class="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
263261 <!-- Linked when we have a real reviewer id (non-anonymous); plain text otherwise. -->
264 <StxLink :if="c.author && c.author.id" x-to="`/user/${c.author.id}`" class="font-medium text-gray-900 text-sm hover:underline underline-offset-2" :text="c.author.name"></StxLink>
265 <span :if="!(c.author && c.author.id)" class="font-medium text-gray-700 text-sm" :text="c.author && c.author.name || 'Anonymous reviewer'"></span>
266 <span class="text-gray-400 text-xs" :text="formatDate(c.created_at)"></span>
262 <StxLink :if="c.author && c.author.id" x-to="`/user/${c.author.id}`" class="font-medium text-gray-900 text-sm hover:underline underline-offset-2">{{ c.author.name }}</StxLink>
263 <span :if="!(c.author && c.author.id)" class="font-medium text-gray-700 text-sm">{{ c.author && c.author.name || 'Anonymous reviewer' }}</span>
264 <span class="text-gray-400 text-xs">{{ formatDate(c.created_at) }}</span>
267265 </div>
268 <p class="mt-1 leading-relaxed text-gray-700 text-sm" :text="c.body"></p>
266 <p class="mt-1 leading-relaxed text-gray-700 text-sm">{{ c.body }}</p>
269267 <button
270268 :if="isMyComment(c)"
271269 type="button"
resources/components/Bench/Court/CourtHeader.stxmodified+3-3
Changes to resources/components/Bench/Court/CourtHeader.stx
@@ -80,9 +80,9 @@
8080 <div class="flex gap-x-6 items-center">
8181 <img x-src="courtImage" x-alt="courtName" class="object-cover flex-none h-16 w-16 ring-1 ring-gray-900/10 rounded-lg">
8282 <div>
83 <div class="text-gray-500 text-sm/6">Courthouse <span class="text-gray-700" :text="`#${courtHeaderId}`"></span></div>
84 <h1 class="mt-1 font-semibold text-base text-gray-900" :text="courtName"></h1>
85 <p class="mt-0.5 text-gray-500 text-sm" :text="`${courtCity}${courtCity && courtState ? ', ' : ''}${courtState}`"></p>
83 <div class="text-gray-500 text-sm/6">Courthouse <span class="text-gray-700">{{ `#${courtHeaderId}` }}</span></div>
84 <h1 class="mt-1 font-semibold text-base text-gray-900">{{ courtName }}</h1>
85 <p class="mt-0.5 text-gray-500 text-sm">{{ `${courtCity}${courtCity && courtState ? ', ' : ''}${courtState}` }}</p>
8686 </div>
8787 </div>
8888 </div>
resources/components/Bench/Court/CourtHouseDirectory.stxmodified+10-10
Changes to resources/components/Bench/Court/CourtHouseDirectory.stx
@@ -136,7 +136,7 @@
136136 x-class="stateFilter === 'all' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
137137 >
138138 All states
139 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="stateFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="courtCounts.byState.all"></span>
139 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="stateFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ courtCounts.byState.all }}</span>
140140 </button>
141141 <button :for="s in availableStates"
142142 type="button"
@@ -145,8 +145,8 @@
145145 class="inline-flex items-center gap-2 px-3 py-1.5 font-medium text-sm rounded-full border transition-colors"
146146 x-class="stateFilter === s ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
147147 >
148 <span :text="s"></span>
149 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="stateFilter === s ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="courtCounts.byState[s] || 0"></span>
148 <span>{{ s }}</span>
149 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="stateFilter === s ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ courtCounts.byState[s] || 0 }}</span>
150150 </button>
151151 </div>
152152
@@ -168,7 +168,7 @@
168168 x-class="hasReviewsFilter === 'with' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
169169 >
170170 With reviews
171 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="hasReviewsFilter === 'with' ? 'bg-white/15 text-white' : 'bg-emerald-50 text-emerald-700'" :text="courtCounts.withReviews"></span>
171 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="hasReviewsFilter === 'with' ? 'bg-white/15 text-white' : 'bg-emerald-50 text-emerald-700'">{{ courtCounts.withReviews }}</span>
172172 </button>
173173 <button
174174 type="button"
@@ -178,7 +178,7 @@
178178 x-class="hasReviewsFilter === 'without' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
179179 >
180180 Without reviews
181 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="hasReviewsFilter === 'without' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="courtCounts.withoutReviews"></span>
181 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="hasReviewsFilter === 'without' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ courtCounts.withoutReviews }}</span>
182182 </button>
183183 </div>
184184
@@ -213,16 +213,16 @@
213213 >
214214 <div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
215215 <div class="absolute bottom-4 left-4">
216 <span class="inline-flex items-center px-3 py-1 font-medium text-gray-700 text-sm bg-white/90 ring-1 ring-gray-700/10 ring-inset rounded-full" :text="court.state"></span>
216 <span class="inline-flex items-center px-3 py-1 font-medium text-gray-700 text-sm bg-white/90 ring-1 ring-gray-700/10 ring-inset rounded-full">{{ court.state }}</span>
217217 </div>
218218 </div>
219219
220220 <!-- Content Container -->
221221 <div class="p-6">
222222 <div class="text-center">
223 <h3 class="font-semibold text-gray-900 text-xl group-hover:text-deep-brown duration-200 transition-colors" :text="court.name"></h3>
224 <p class="mt-2 text-gray-500 text-sm" :text="`${court.city}, ${court.state}`"></p>
225 <p class="mt-1 text-gray-500 text-sm" :text="court.address"></p>
223 <h3 class="font-semibold text-gray-900 text-xl group-hover:text-deep-brown duration-200 transition-colors">{{ court.name }}</h3>
224 <p class="mt-2 text-gray-500 text-sm">{{ `${court.city}, ${court.state}` }}</p>
225 <p class="mt-1 text-gray-500 text-sm">{{ court.address }}</p>
226226 </div>
227227 </div>
228228 </StxLink>
@@ -236,7 +236,7 @@
236236 <svg class="mx-auto h-12 w-12 text-gray-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
237237 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 21h16.5M4.5 3h15M5.25 3v18m13.5-18v18M9 6.75h1.5m-1.5 3h1.5m-1.5 3h1.5m3-6H15m-1.5 3H15m-1.5 3H15M9 21v-3.375c0-.621.504-1.125 1.125-1.125h3.75c.621 0 1.125.504 1.125 1.125V21" />
238238 </svg>
239 <p class="mt-4 font-medium text-gray-900" :text="(stateFilter() !== 'all' || hasReviewsFilter() !== 'all' || searchQuery() !== '') ? 'No courthouses match these filters' : 'No courthouses yet'"></p>
239 <p class="mt-4 font-medium text-gray-900">{{ (stateFilter() !== 'all' || hasReviewsFilter() !== 'all' || searchQuery() !== '') ? 'No courthouses match these filters' : 'No courthouses yet' }}</p>
240240 <p x-class="hasActiveFilters() ? '' : 'hidden'" class="mt-1 text-gray-500 text-sm">Try a different state, or clear the filters to see all courthouses.</p>
241241 <button
242242 x-class="hasActiveFilters() ? '' : 'hidden'"
resources/components/Bench/Court/CourtJudges.stxmodified+4-4
Changes to resources/components/Bench/Court/CourtJudges.stx
@@ -19,7 +19,7 @@
1919 <div class="flex items-center justify-between">
2020 <div>
2121 <h2 class="font-semibold text-2xl text-gray-900">Judges at this Court</h2>
22 <p class="mt-1 text-gray-500 text-sm" :text="`Active judges currently sitting at ${court.name}`"></p>
22 <p class="mt-1 text-gray-500 text-sm">{{ `Active judges currently sitting at ${court.name}` }}</p>
2323 </div>
2424 <div class="hidden sm:block">
2525 <select class="pl-3 pr-10 py-2 text-gray-900 sm:text-sm bg-white ring-1 ring-gray-300 ring-inset rounded-md focus:ring-2 focus:ring-gray-600 focus:ring-inset">
@@ -37,14 +37,14 @@
3737 <StxLink x-to="`/judges/${judge.id}/profile`" class="flex flex-1 gap-x-4 items-center min-w-0">
3838 <img class="object-cover flex-none h-12 w-12 rounded-full grayscale filter" x-src="judge.photo" x-alt="judge.name">
3939 <div class="flex-auto min-w-0">
40 <p class="font-semibold text-gray-900 text-sm hover:underline" :text="judge.name"></p>
41 <p class="mt-0.5 text-gray-500 text-xs truncate" :text="judge.department"></p>
40 <p class="font-semibold text-gray-900 text-sm hover:underline">{{ judge.name }}</p>
41 <p class="mt-0.5 text-gray-500 text-xs truncate">{{ judge.department }}</p>
4242 </div>
4343 </StxLink>
4444 <div class="flex gap-x-4 items-center sm:gap-x-6">
4545 <div class="hidden sm:flex sm:items-center">
4646 <Icon :for="star in stars" name="star" size="16" x-class="star <= judge.rating ? 'text-yellow-400' : 'text-gray-200'" />
47 <span class="ml-2 text-gray-500 text-xs" :text="judge.reviewCount"></span>
47 <span class="ml-2 text-gray-500 text-xs">{{ judge.reviewCount }}</span>
4848 </div>
4949 <StxLink x-to="`/judges/${judge.id}/profile`" class="font-semibold text-gray-700 text-sm hover:text-gray-900">
5050 View<span aria-hidden="true"> →</span>
resources/components/Bench/Court/CourtProfile.stxmodified+5-5
Changes to resources/components/Bench/Court/CourtProfile.stx
@@ -173,7 +173,7 @@
173173
174174<div class="lg:col-span-2 lg:row-end-2 lg:row-span-2 -mx-4 px-4 py-8 sm:mx-0 sm:pb-14 sm:px-8 xl:pb-20 xl:pt-16 xl:px-16 bg-white ring-1 ring-gray-900/5 sm:rounded-lg shadow-sm">
175175 <h2 class="font-semibold text-base text-gray-800">About this court</h2>
176 <p class="mt-4 leading-7 text-gray-600" :text="aboutText"></p>
176 <p class="mt-4 leading-7 text-gray-600">{{ aboutText }}</p>
177177
178178 <dl class="grid grid-cols-1 sm:grid-cols-2 mt-8 text-sm/6">
179179 <div class="sm:pr-4">
@@ -204,22 +204,22 @@
204204 <dl class="grid gap-4 grid-cols-1 sm:grid-cols-3 mt-4">
205205 <div class="px-4 py-3 bg-off-gray rounded-lg">
206206 <dt class="text-gray-500 text-xs">Active Judges</dt>
207 <dd class="mt-1 font-semibold text-2xl text-gray-900" :text="activeJudges"></dd>
207 <dd class="mt-1 font-semibold text-2xl text-gray-900">{{ activeJudges }}</dd>
208208 </div>
209209 <div class="px-4 py-3 bg-off-gray rounded-lg">
210210 <dt class="text-gray-500 text-xs">Published Reviews</dt>
211 <dd class="mt-1 font-semibold text-2xl text-gray-900" :text="totalReviews"></dd>
211 <dd class="mt-1 font-semibold text-2xl text-gray-900">{{ totalReviews }}</dd>
212212 </div>
213213 <div class="px-4 py-3 bg-off-gray rounded-lg">
214214 <dt class="text-gray-500 text-xs">Avg Rating</dt>
215 <dd class="mt-1 font-semibold text-2xl text-gray-900" :text="avgRating"></dd>
215 <dd class="mt-1 font-semibold text-2xl text-gray-900">{{ avgRating }}</dd>
216216 </div>
217217 </dl>
218218 </div>
219219
220220 <div class="mt-12">
221221 <h3 class="font-semibold text-base text-gray-800">Visit this court</h3>
222 <p class="mt-1 text-gray-500 text-xs" :text="courtAddress"></p>
222 <p class="mt-1 text-gray-500 text-xs">{{ courtAddress }}</p>
223223
224224 <!-- Map host. Leaflet measures the element at construction time,
225225 so the explicit h-64 (256px) on the host is required — without
resources/components/Bench/Court/CourtReviews.stxmodified+5-5
Changes to resources/components/Bench/Court/CourtReviews.stx
@@ -54,20 +54,20 @@
5454 <div :for="review in courtReviews()" class="pb-8 border-b border-gray-200">
5555 <div class="flex items-center justify-between">
5656 <div class="flex items-center">
57 <div class="flex items-center justify-center h-10 w-10 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full" :text="(review.title || 'A').slice(0, 1).toUpperCase()"></div>
57 <div class="flex items-center justify-center h-10 w-10 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full">{{ (review.title || 'A').slice(0, 1).toUpperCase() }}</div>
5858 <div class="ml-4">
59 <h4 class="font-semibold text-gray-900 text-sm" :text="review.title"></h4>
59 <h4 class="font-semibold text-gray-900 text-sm">{{ review.title }}</h4>
6060 <div class="flex items-center">
6161 <div class="flex items-center">
6262 <Icon :for="star in stars" name="star" size="16" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-300'" />
6363 </div>
64 <span class="ml-2 text-gray-500 text-sm" :text="formatDate(review.created_at)"></span>
64 <span class="ml-2 text-gray-500 text-sm">{{ formatDate(review.created_at) }}</span>
6565 </div>
6666 </div>
6767 </div>
68 <span :if="review.type" class="inline-flex items-center px-2 py-1 font-medium text-xs text-gray-700 bg-gray-100 ring-1 ring-gray-200 ring-inset rounded-full" :text="review.type"></span>
68 <span :if="review.type" class="inline-flex items-center px-2 py-1 font-medium text-xs text-gray-700 bg-gray-100 ring-1 ring-gray-200 ring-inset rounded-full">{{ review.type }}</span>
6969 </div>
70 <p class="mt-3 leading-relaxed line-clamp-3 text-gray-700 text-sm" :text="review.content"></p>
70 <p class="mt-3 leading-relaxed line-clamp-3 text-gray-700 text-sm">{{ review.content }}</p>
7171 <div class="flex items-center justify-between mt-4">
7272 <StxLink x-to="`/judges/${review.judge_id}/profile`" class="text-gray-600 text-sm hover:underline">
7373 View judge →
resources/components/Bench/Feed.stxmodified+8-8
Changes to resources/components/Bench/Feed.stx
@@ -24,19 +24,19 @@
2424 </StxLink>
2525 <div class="ml-2">
2626 <p class="font-medium text-gray-900 text-sm">
27 <StxLink x-to="`/profile/${review.author.id}`" class="hover:underline" :text="review.author.name"></StxLink>
27 <StxLink x-to="`/profile/${review.author.id}`" class="hover:underline">{{ review.author.name }}</StxLink>
2828 </p>
2929 <p class="text-gray-500 text-xs">reviewed</p>
3030 </div>
3131 </div>
32 <span class="text-gray-600 text-sm" :text="review.date"></span>
32 <span class="text-gray-600 text-sm">{{ review.date }}</span>
3333 </div>
3434
3535 <!-- Review Title and Content -->
3636 <div class="mt-3">
37 <h3 class="mb-2 font-bold text-gray-900 text-xl" :text="review.title"></h3>
37 <h3 class="mb-2 font-bold text-gray-900 text-xl">{{ review.title }}</h3>
3838 <div class="relative">
39 <p class="leading-relaxed line-clamp-3 text-base text-gray-700" :text="review.content"></p>
39 <p class="leading-relaxed line-clamp-3 text-base text-gray-700">{{ review.content }}</p>
4040 @include('Bench/BlurReview')
4141 </div>
4242 </div>
@@ -44,7 +44,7 @@
4444 <!-- Judge Name and Rating -->
4545 <div class="flex items-center justify-between mt-4">
4646 <div class="flex items-center space-x-3">
47 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="font-semibold text-base text-gray-900 hover:underline" :text="review.judge.name"></StxLink>
47 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="font-semibold text-base text-gray-900 hover:underline">{{ review.judge.name }}</StxLink>
4848 <div class="flex items-center">
4949 <Icon :for="star in stars" name="star" size="16" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-300'" />
5050 </div>
@@ -57,7 +57,7 @@
5757 x-class="isLiked() ? 'text-blue-600' : 'text-gray-500 hover:text-gray-600'"
5858 >
5959 <Icon name="bookmark" size="20" class="mr-1.5 transition-colors" x-class="isLiked() ? 'text-blue-600' : 'text-gray-500 hover:text-gray-600'" />
60 <span x-class="isLiked() ? 'text-blue-600' : 'hover:text-gray-600'" :text="review.likes"></span>
60 <span x-class="isLiked() ? 'text-blue-600' : 'hover:text-gray-600'">{{ review.likes }}</span>
6161 </button>
6262 <StxLink
6363 x-to="`/article/${review.id}`"
@@ -70,8 +70,8 @@
7070
7171 <!-- Status Tags -->
7272 <div class="flex items-center mt-4 space-x-2">
73 <span class="inline-flex items-center px-2 py-1 font-medium text-deepBrown text-xs bg-offBrown ring-1 ring-deepBrown/20 ring-inset rounded-full" :text="review.type"></span>
74 <span class="inline-flex items-center px-2 py-1 font-medium text-deepBrown text-xs bg-offBrown ring-1 ring-deepBrown/20 ring-inset rounded-full" :text="review.status"></span>
73 <span class="inline-flex items-center px-2 py-1 font-medium text-deepBrown text-xs bg-offBrown ring-1 ring-deepBrown/20 ring-inset rounded-full">{{ review.type }}</span>
74 <span class="inline-flex items-center px-2 py-1 font-medium text-deepBrown text-xs bg-offBrown ring-1 ring-deepBrown/20 ring-inset rounded-full">{{ review.status }}</span>
7575 </div>
7676 </div>
7777
resources/components/Bench/Judge/Cases.stxmodified+6-8
Changes to resources/components/Bench/Judge/Cases.stx
@@ -107,21 +107,19 @@
107107 </thead>
108108 <tbody class="divide-gray-200 divide-y">
109109 <tr :for="case_ in cases">
110 <td class="pl-4 pr-3 py-4 sm:pl-0 font-medium text-gray-900 text-sm whitespace-nowrap" :text="case_.number"></td>
111 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap" :text="case_.title"></td>
110 <td class="pl-4 pr-3 py-4 sm:pl-0 font-medium text-gray-900 text-sm whitespace-nowrap">{{ case_.number }}</td>
111 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">{{ case_.title }}</td>
112112 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">
113 <span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs bg-blue-50 ring-1 ring-blue-700/10 ring-inset rounded-md" :text="case_.type"></span>
113 <span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs bg-blue-50 ring-1 ring-blue-700/10 ring-inset rounded-md">{{ case_.type }}</span>
114114 </td>
115115 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">
116116 <span
117117 class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset"
118 x-class="statusClass(case_.status)"
119 :text="case_.status"
120 ></span>
118 x-class="statusClass(case_.status)">{{ case_.status }}</span>
121119 </td>
122 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap" :text="case_.filedDate"></td>
120 <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">{{ case_.filedDate }}</td>
123121 <td class="relative pl-3 pr-4 py-4 sm:pr-0 font-medium text-right text-sm whitespace-nowrap">
124 <a href="#" class="text-gray-600 hover:text-gray-900">View<span class="sr-only" :text="`, ${case_.number}`"></span></a>
122 <a href="#" class="text-gray-600 hover:text-gray-900">View<span class="sr-only">{{ `, ${case_.number}` }}</span></a>
125123 </td>
126124 </tr>
127125 </tbody>
resources/components/Bench/Judge/JudgeDirectory.stxmodified+8-14
Changes to resources/components/Bench/Judge/JudgeDirectory.stx
@@ -180,7 +180,7 @@
180180 x-class="practiceFilter === 'all' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
181181 >
182182 All
183 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="practiceFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="practiceCounts.all"></span>
183 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="practiceFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ practiceCounts.all }}</span>
184184 </button>
185185 <button :for="area in PRACTICE_AREAS"
186186 type="button"
@@ -189,8 +189,8 @@
189189 class="inline-flex items-center gap-2 px-3 py-1.5 font-medium text-sm rounded-full border transition-colors capitalize"
190190 x-class="practiceFilter === area ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
191191 >
192 <span :text="area"></span>
193 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="practiceFilter === area ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="practiceCounts[area]"></span>
192 <span>{{ area }}</span>
193 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="practiceFilter === area ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ practiceCounts[area] }}</span>
194194 </button>
195195 </div>
196196
@@ -230,9 +230,7 @@
230230 role="tab"
231231 @click="setStateFilter(s)"
232232 class="inline-flex items-center px-3 py-1.5 font-medium text-sm rounded-full border transition-colors"
233 x-class="stateFilter === s ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
234 :text="s"
235 ></button>
233 x-class="stateFilter === s ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'">{{ s }}</button>
236234 </div>
237235
238236 <!-- Rating-threshold + activity filter rows. URL-driven via
@@ -325,15 +323,11 @@
325323 </div>
326324
327325 <span
328 class="inline-flex items-center mt-3 px-3 py-1 font-medium text-gray-700 text-xs bg-gray-50 ring-1 ring-gray-200 ring-inset rounded-full"
329 :text="judge.court.name"
330 ></span>
326 class="inline-flex items-center mt-3 px-3 py-1 font-medium text-gray-700 text-xs bg-gray-50 ring-1 ring-gray-200 ring-inset rounded-full">{{ judge.court.name }}</span>
331327
332328 <h3
333 class="mt-3 font-semibold text-gray-900 text-lg group-hover:text-gray-700 duration-200 transition-colors"
334 :text="judge.name"
335 ></h3>
336 <p class="mt-1 text-gray-500 text-sm" :text="judge.location"></p>
329 class="mt-3 font-semibold text-gray-900 text-lg group-hover:text-gray-700 duration-200 transition-colors">{{ judge.name }}</h3>
330 <p class="mt-1 text-gray-500 text-sm">{{ judge.location }}</p>
337331
338332 <!-- Review aggregates are lazy-loaded by /api/judges/:id/reviews
339333 when a profile opens — they're NOT included on the
@@ -353,7 +347,7 @@
353347 <svg class="mx-auto h-12 w-12 text-gray-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
354348 <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
355349 </svg>
356 <p class="mt-4 font-medium text-gray-900" :text="(practiceFilter() !== 'all' || stateFilter() !== 'all' || ratingFilter() !== 'all' || hasReviewsFilter() !== 'all' || searchQuery() !== '') ? 'No judges match these filters' : 'No judges yet'"></p>
350 <p class="mt-4 font-medium text-gray-900">{{ (practiceFilter() !== 'all' || stateFilter() !== 'all' || ratingFilter() !== 'all' || hasReviewsFilter() !== 'all' || searchQuery() !== '') ? 'No judges match these filters' : 'No judges yet' }}</p>
357351 <p x-class="hasActiveFilters() ? '' : 'hidden'" class="mt-1 text-gray-500 text-sm">Try a different practice area or state, or clear the filters to see all judges.</p>
358352 <button
359353 x-class="hasActiveFilters() ? '' : 'hidden'"
resources/components/Bench/Judge/ProfileHeader.stxmodified+3-4
Changes to resources/components/Bench/Judge/ProfileHeader.stx
@@ -125,8 +125,8 @@
125125 <div class="flex gap-x-6 items-center">
126126 <img x-src="judgeImage" x-alt="judgeName" class="object-cover flex-none size-16 ring-1 ring-gray-900/10 rounded-full grayscale filter">
127127 <h1>
128 <div class="text-gray-500 text-sm/6">Judge <span class="text-gray-700" :text="`#${judgeId}`"></span></div>
129 <div class="mt-1 font-semibold text-base text-gray-900" :text="judgeName"></div>
128 <div class="text-gray-500 text-sm/6">Judge <span class="text-gray-700">{{ `#${judgeId}` }}</span></div>
129 <div class="mt-1 font-semibold text-base text-gray-900">{{ judgeName }}</div>
130130 </h1>
131131 </div>
132132 <div class="flex gap-x-4 items-center sm:gap-x-6">
@@ -149,8 +149,7 @@
149149 ? 'bg-white text-gray-900 ring-1 ring-gray-300 hover:bg-gray-50 focus-visible:outline-gray-300'
150150 : 'bg-gray-900 text-white hover:bg-gray-700 focus-visible:outline-gray-900'
151151 ]"
152 :text="followPending ? '…' : (isFollowing ? 'Following' : 'Follow')"
153 >Follow</button>
152 >{{ followPending ? '…' : (isFollowing ? 'Following' : 'Follow') }}</button>
154153
155154 <div class="sm:hidden relative">
156155 <button type="button" class="block -m-3 p-3" @click="menuOpen.set(!menuOpen())" id="more-menu-button" aria-expanded="false" aria-haspopup="true">
resources/components/Bench/Judge/RecentRulings.stxmodified+6-8
Changes to resources/components/Bench/Judge/RecentRulings.stx
@@ -105,23 +105,21 @@
105105 <li :for="o in opinions" class="p-5 bg-white border border-gray-200 rounded-lg hover:border-gray-300 hover:shadow-sm transition-all">
106106 <div class="flex flex-wrap items-start justify-between gap-3">
107107 <div class="min-w-0 flex-1">
108 <h3 class="font-semibold text-gray-900 text-base" :text="o.case_name"></h3>
108 <h3 class="font-semibold text-gray-900 text-base">{{ o.case_name }}</h3>
109109 <div class="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-gray-500 text-xs">
110 <span :if="o.decision_date" :text="formatDate(o.decision_date)"></span>
110 <span :if="o.decision_date">{{ formatDate(o.decision_date) }}</span>
111111 <span :if="o.decision_date && o.citation" class="text-gray-300">·</span>
112 <span :if="o.citation" class="font-mono text-gray-600" :text="o.citation"></span>
112 <span :if="o.citation" class="font-mono text-gray-600">{{ o.citation }}</span>
113113 <span :if="(o.decision_date || o.citation) && o.source_provider !== 'manual'" class="text-gray-300">·</span>
114 <span :if="o.source_provider !== 'manual'" class="text-gray-500" :text="`via ${o.source_provider}`"></span>
114 <span :if="o.source_provider !== 'manual'" class="text-gray-500">{{ `via ${o.source_provider}` }}</span>
115115 </div>
116116 </div>
117117 <span
118118 :if="o.outcome_label"
119119 class="inline-flex items-center px-2.5 py-0.5 font-medium text-xs ring-1 rounded-full capitalize whitespace-nowrap"
120 x-class="outcomeBadgeClasses(o.outcome_label)"
121 :text="o.outcome_label"
122 ></span>
120 x-class="outcomeBadgeClasses(o.outcome_label)">{{ o.outcome_label }}</span>
123121 </div>
124 <p :if="o.summary" class="mt-3 leading-relaxed text-gray-700 text-sm" :text="o.summary"></p>
122 <p :if="o.summary" class="mt-3 leading-relaxed text-gray-700 text-sm">{{ o.summary }}</p>
125123 <a
126124 :if="o.source_url"
127125 x-href="o.source_url"
resources/components/Bench/Judge/ReviewJudgeSearch.stxmodified+4-4
Changes to resources/components/Bench/Judge/ReviewJudgeSearch.stx
@@ -136,9 +136,9 @@
136136 >
137137 <img x-src="result.photo" class="object-cover flex-none h-10 w-10 ring-1 ring-gray-100 rounded-full grayscale filter" x-alt="result.name">
138138 <div class="min-w-0 flex-1">
139 <p class="font-medium text-gray-900 text-sm truncate" :text="result.name"></p>
140 <p class="text-gray-500 text-xs truncate" :text="result.court.name"></p>
141 <p :if="result.location" class="text-gray-400 text-xs truncate" :text="result.location"></p>
139 <p class="font-medium text-gray-900 text-sm truncate">{{ result.name }}</p>
140 <p class="text-gray-500 text-xs truncate">{{ result.court.name }}</p>
141 <p :if="result.location" class="text-gray-400 text-xs truncate">{{ result.location }}</p>
142142 </div>
143143 <svg class="h-4 w-4 text-gray-300 flex-none" viewBox="0 0 20 20" fill="currentColor">
144144 <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
@@ -148,7 +148,7 @@
148148 </div>
149149
150150 <div :if="showEmpty" class="mt-2 p-4 text-center bg-gray-50 border border-gray-200 border-dashed rounded-md">
151 <p class="text-gray-600 text-sm">No judges match "<span :text="searchInput()"></span>".</p>
151 <p class="text-gray-600 text-sm">No judges match "<span></span>".</p>
152152 <p class="mt-1 text-gray-400 text-xs">Try a different name or court.</p>
153153 </div>
154154 </div>
resources/components/Bench/Judge/Reviews.stxmodified+9-9
Changes to resources/components/Bench/Judge/Reviews.stx
@@ -52,12 +52,12 @@
5252 <!-- Header: summary if reviews exist, write-CTA always -->
5353 <div class="flex items-center justify-between">
5454 <div :if="reviewCount() > 0">
55 <h2 class="font-semibold text-2xl text-gray-900" :text="averageRating()"></h2>
55 <h2 class="font-semibold text-2xl text-gray-900">{{ averageRating() }}</h2>
5656 <div class="flex items-center">
5757 <div class="flex items-center">
5858 <Icon :for="star in stars" name="star" size="20" x-class="star <= Math.round(averageRating()) ? 'text-yellow-400' : 'text-gray-300'" />
5959 </div>
60 <p class="ml-2 text-gray-500 text-sm" :text="`Based on ${reviewCount()} review${reviewCount() === 1 ? '' : 's'}`"></p>
60 <p class="ml-2 text-gray-500 text-sm">{{ `Based on ${reviewCount()} review${reviewCount() === 1 ? '' : 's'}` }}</p>
6161 </div>
6262 </div>
6363 <div :if="reviewCount() === 0">
@@ -90,13 +90,13 @@
9090 <h3 class="font-semibold text-base text-gray-900">Rating Distribution</h3>
9191 <div class="mt-4 space-y-3">
9292 <div :for="bucket in ratingBuckets()" class="flex items-center">
93 <span class="text-gray-500 text-sm" :text="`${bucket.stars} stars`"></span>
93 <span class="text-gray-500 text-sm">{{ `${bucket.stars} stars` }}</span>
9494 <div class="flex-1 ml-4">
9595 <div class="h-2 bg-gray-200 rounded-full">
9696 <div class="h-2 bg-yellow-400 rounded-full" x-style="`width: ${bucket.percentage}%`"></div>
9797 </div>
9898 </div>
99 <span class="ml-4 text-gray-500 text-sm" :text="`${bucket.percentage}%`"></span>
99 <span class="ml-4 text-gray-500 text-sm">{{ `${bucket.percentage}%` }}</span>
100100 </div>
101101 </div>
102102 </div>
@@ -107,14 +107,14 @@
107107 <div :for="review in reviews()" class="pb-8 border-b border-gray-200">
108108 <div class="flex items-center justify-between">
109109 <div class="flex items-center">
110 <div class="flex items-center justify-center h-10 w-10 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full" :text="(review.title || 'A').slice(0, 1).toUpperCase()"></div>
110 <div class="flex items-center justify-center h-10 w-10 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full">{{ (review.title || 'A').slice(0, 1).toUpperCase() }}</div>
111111 <div class="ml-4">
112 <h4 class="font-semibold text-gray-900 text-sm" :text="review.title"></h4>
112 <h4 class="font-semibold text-gray-900 text-sm">{{ review.title }}</h4>
113113 <div class="flex items-center">
114114 <div class="flex items-center">
115115 <Icon :for="star in stars" name="star" size="16" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-300'" />
116116 </div>
117 <span class="ml-2 text-gray-500 text-sm" :text="formatDate(review.created_at)"></span>
117 <span class="ml-2 text-gray-500 text-sm">{{ formatDate(review.created_at) }}</span>
118118 </div>
119119 </div>
120120 </div>
@@ -123,10 +123,10 @@
123123 </button>
124124 </div>
125125 <div class="mt-4 text-gray-500 text-sm">
126 <p :text="review.content"></p>
126 <p>{{ review.content }}</p>
127127 </div>
128128 <div class="flex items-center mt-4 space-x-4">
129 <button type="button" class="font-semibold text-gray-500 text-sm hover:text-gray-700" :text="`Helpful (${review.likes ?? 0})`"></button>
129 <button type="button" class="font-semibold text-gray-500 text-sm hover:text-gray-700">{{ `Helpful (${review.likes ?? 0})` }}</button>
130130 </div>
131131 </div>
132132 </div>
resources/components/Bench/JudgeSignup.stxmodified+7-9
Changes to resources/components/Bench/JudgeSignup.stx
@@ -116,7 +116,7 @@
116116 <div x-class="isAuthed() && phase() === 'claimed' ? '' : 'hidden'" class="mt-8 p-6 text-center bg-white border border-amber-200 rounded-2xl shadow-sm">
117117 <h2 class="font-serif font-bold text-2xl text-emerald-700">Claim submitted ✓</h2>
118118 <p class="mt-2 text-gray-700 text-sm">
119 Thanks — we've recorded your claim for <span class="font-semibold" :text="claimedName()"></span>. An admin will verify your identity before you can respond to reviews. We'll be in touch.
119 Thanks — we've recorded your claim for <span class="font-semibold"></span>. An admin will verify your identity before you can respond to reviews. We'll be in touch.
120120 </p>
121121 <StxLink to="/" class="inline-block mt-5 font-semibold text-gray-900 text-sm hover:underline underline-offset-4">← Back home</StxLink>
122122 </div>
@@ -144,10 +144,10 @@
144144 @click="pick(j)"
145145 class="flex w-full items-center gap-3 px-3 py-2 text-left hover:bg-gray-50"
146146 >
147 <span class="inline-flex flex-none items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full" :text="(j.name || '?').charAt(0)"></span>
147 <span class="inline-flex flex-none items-center justify-center h-8 w-8 font-semibold text-amber-700 text-xs bg-amber-50 ring-1 ring-amber-200/60 rounded-full">{{ (j.name || '?').charAt(0) }}</span>
148148 <span class="min-w-0">
149 <span class="block font-medium text-gray-900 text-sm" :text="j.name"></span>
150 <span class="block text-gray-500 text-xs" :text="j.location || (j.court ? j.court.name : '')"></span>
149 <span class="block font-medium text-gray-900 text-sm">{{ j.name }}</span>
150 <span class="block text-gray-500 text-xs">{{ j.location || (j.court ? j.court.name : '') }}</span>
151151 </span>
152152 </button>
153153 </li>
@@ -156,7 +156,7 @@
156156 <!-- Selected confirmation + submit -->
157157 <div x-class="hasSelection() ? '' : 'hidden'" class="mt-4 p-3 bg-amber-50/60 border border-amber-200 rounded-md">
158158 <p class="text-gray-700 text-sm">
159 Claiming: <span class="font-semibold" :text="selected() ? selected().name : ''"></span>
159 Claiming: <span class="font-semibold">{{ selected() ? selected().name : '' }}</span>
160160 </p>
161161 <button
162162 type="button"
@@ -165,16 +165,14 @@
165165 class="mt-3 inline-flex items-center justify-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
166166 >
167167 <svg :if="submitting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
168 <span :text="submitting() ? 'Submitting…' : 'Submit claim for verification'"></span>
168 <span>{{ submitting() ? 'Submitting…' : 'Submit claim for verification' }}</span>
169169 </button>
170170 </div>
171171
172172 <div
173173 role="alert"
174174 x-class="errorMsg() ? '' : 'hidden'"
175 class="mt-3 px-3 py-2 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md"
176 :text="errorMsg()"
177 ></div>
175 class="mt-3 px-3 py-2 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md">{{ errorMsg() }}</div>
178176
179177 <p class="mt-4 text-gray-400 text-xs leading-relaxed">
180178 Can't find your profile? It may not be in our directory yet. Contact us and we'll add it.
resources/components/Bench/LeftSidebar.stxmodified+5-5
Changes to resources/components/Bench/LeftSidebar.stx
@@ -15,8 +15,8 @@
1515 <div :for="judge in trendingJudges" class="flex items-center space-x-3">
1616 <img class="h-8 w-8 rounded-full grayscale filter" x-src="judge.photo" x-alt="judge.name" />
1717 <div class="flex-1 min-w-0">
18 <p class="font-medium text-gray-900 text-sm" :text="judge.name"></p>
19 <p class="text-gray-500 text-sm" :text="judge.court"></p>
18 <p class="font-medium text-gray-900 text-sm">{{ judge.name }}</p>
19 <p class="text-gray-500 text-sm">{{ judge.court }}</p>
2020 </div>
2121 <button type="button" class="font-semibold text-gray-900 text-xs hover:underline">Follow</button>
2222 </div>
@@ -36,15 +36,15 @@
3636 <div class="flex relative space-x-3">
3737 <div>
3838 <span class="flex items-center justify-center h-8 w-8 bg-gray-400 ring-8 ring-white rounded-full">
39 <span class="h-5 w-5 text-white" aria-hidden="true" :text="activity.icon"></span>
39 <span class="h-5 w-5 text-white" aria-hidden="true">{{ activity.icon }}</span>
4040 </span>
4141 </div>
4242 <div class="flex flex-1 justify-between pt-1.5 space-x-4 min-w-0">
4343 <div>
44 <p class="text-gray-500 text-sm" :text="activity.content"></p>
44 <p class="text-gray-500 text-sm">{{ activity.content }}</p>
4545 </div>
4646 <div class="text-gray-500 text-right text-sm whitespace-nowrap">
47 <time :datetime="activity.dateTime" :text="activity.date"></time>
47 <time :datetime="activity.dateTime">{{ activity.date }}</time>
4848 </div>
4949 </div>
5050 </div>
resources/components/Bench/MyReviewsView.stxmodified+13-19
Changes to resources/components/Bench/MyReviewsView.stx
@@ -151,7 +151,7 @@
151151 x-class="statusFilter === 'all' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
152152 >
153153 All
154 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'" :text="counts.all"></span>
154 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'all' ? 'bg-white/15 text-white' : 'bg-gray-100 text-gray-600'">{{ counts.all }}</span>
155155 </button>
156156 <button
157157 type="button"
@@ -161,7 +161,7 @@
161161 x-class="statusFilter === 'pending' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
162162 >
163163 Pending
164 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'pending' ? 'bg-white/15 text-white' : 'bg-amber-50 text-amber-700'" :text="counts.pending"></span>
164 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'pending' ? 'bg-white/15 text-white' : 'bg-amber-50 text-amber-700'">{{ counts.pending }}</span>
165165 </button>
166166 <button
167167 type="button"
@@ -171,7 +171,7 @@
171171 x-class="statusFilter === 'published' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
172172 >
173173 Published
174 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'published' ? 'bg-white/15 text-white' : 'bg-emerald-50 text-emerald-700'" :text="counts.published"></span>
174 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'published' ? 'bg-white/15 text-white' : 'bg-emerald-50 text-emerald-700'">{{ counts.published }}</span>
175175 </button>
176176 <button
177177 type="button"
@@ -181,7 +181,7 @@
181181 x-class="statusFilter === 'rejected' ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300'"
182182 >
183183 Declined
184 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'rejected' ? 'bg-white/15 text-white' : 'bg-red-50 text-red-700'" :text="counts.rejected"></span>
184 <span class="px-1.5 py-0.5 tabular-nums text-xs rounded" x-class="statusFilter === 'rejected' ? 'bg-white/15 text-white' : 'bg-red-50 text-red-700'">{{ counts.rejected }}</span>
185185 </button>
186186 </div>
187187
@@ -215,8 +215,8 @@
215215 initial load to avoid the "no reviews" flash before the
216216 first fetch returns. -->
217217 <div :if="profileStore.loaded && filtered.length === 0" class="mt-10 p-10 text-center bg-white border border-gray-200 rounded-lg">
218 <p class="font-semibold text-gray-900 text-base" :text="emptyTitle"></p>
219 <p class="mt-2 mx-auto max-w-md text-gray-500 text-sm" :text="emptyHint"></p>
218 <p class="font-semibold text-gray-900 text-base">{{ emptyTitle }}</p>
219 <p class="mt-2 mx-auto max-w-md text-gray-500 text-sm">{{ emptyHint }}</p>
220220 <StxLink to="/review" class="inline-flex items-center mt-6 px-4 py-2 font-medium text-white text-sm bg-gray-900 rounded-md hover:bg-gray-800">
221221 Write a review
222222 </StxLink>
@@ -231,14 +231,10 @@
231231 <div class="flex flex-wrap items-baseline gap-x-3 gap-y-1">
232232 <StxLink
233233 x-to="`/article/${r.id}`"
234 class="font-semibold text-gray-900 text-base hover:underline underline-offset-2 decoration-gray-300"
235 :text="r.title"
236 ></StxLink>
234 class="font-semibold text-gray-900 text-base hover:underline underline-offset-2 decoration-gray-300">{{ r.title }}</StxLink>
237235 <span
238236 class="inline-flex items-center px-2 py-0.5 font-medium text-xs ring-1 rounded-full"
239 x-class="statusBadgeClasses(r.status)"
240 :text="statusLabel(r.status)"
241 ></span>
237 x-class="statusBadgeClasses(r.status)">{{ statusLabel(r.status) }}</span>
242238 </div>
243239
244240 <div class="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-gray-500 text-xs">
@@ -246,19 +242,17 @@
246242 Review of
247243 <StxLink
248244 x-to="`/judges/${r.judge.id}/profile`"
249 class="text-gray-700 hover:underline underline-offset-2"
250 :text="r.judge.name"
251 ></StxLink>
245 class="text-gray-700 hover:underline underline-offset-2">{{ r.judge.name }}</StxLink>
252246 </span>
253247 <span class="text-gray-300">·</span>
254 <span :text="formatDate(r.created_at)"></span>
248 <span>{{ formatDate(r.created_at) }}</span>
255249 <span class="text-gray-300">·</span>
256 <span class="inline-flex items-center gap-1 tabular-nums" :text="`${r.rating}/5`"></span>
250 <span class="inline-flex items-center gap-1 tabular-nums">{{ `${r.rating}/5` }}</span>
257251 <span :if="r.likes && r.likes > 0" class="text-gray-300">·</span>
258 <span :if="r.likes && r.likes > 0" class="inline-flex items-center gap-1" :text="`${r.likes} ${r.likes === 1 ? 'reaction' : 'reactions'}`"></span>
252 <span :if="r.likes && r.likes > 0" class="inline-flex items-center gap-1">{{ `${r.likes} ${r.likes === 1 ? 'reaction' : 'reactions'}` }}</span>
259253 </div>
260254
261 <p class="mt-3 text-gray-600 text-sm line-clamp-2" :text="snippet(r.content)"></p>
255 <p class="mt-3 text-gray-600 text-sm line-clamp-2">{{ snippet(r.content) }}</p>
262256
263257 <div class="flex flex-wrap items-center gap-x-4 mt-4 pt-3 border-t border-gray-100 text-sm">
264258 <StxLink x-to="`/article/${r.id}`" class="font-medium text-gray-900 hover:underline underline-offset-2">
resources/components/Bench/NotificationsView.stxmodified+6-6
Changes to resources/components/Bench/NotificationsView.stx
@@ -83,7 +83,7 @@
8383 <div>
8484 <h1 class="font-bold text-2xl text-gray-900">Notifications</h1>
8585 <p class="mt-1 text-gray-500 text-sm">
86 <span :text="store.unreadCount()"></span> unread
86 <span>{{ store.unreadCount() }}</span> unread
8787 </p>
8888 </div>
8989 <button
@@ -125,8 +125,8 @@
125125 <path d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
126126 </svg>
127127 </div>
128 <p class="font-semibold text-gray-900 text-sm" :text="emptyTitle()"></p>
129 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed" :text="emptyHint()"></p>
128 <p class="font-semibold text-gray-900 text-sm">{{ emptyTitle() }}</p>
129 <p class="mt-1 max-w-md mx-auto text-gray-500 text-xs leading-relaxed">{{ emptyHint() }}</p>
130130 </div>
131131 <ul x-class="store.items().length > 0 ? 'divide-gray-100 divide-y' : 'hidden'">
132132 <li :for="n in store.items" class="hover:bg-gray-50">
@@ -137,9 +137,9 @@
137137 >
138138 <span class="mt-1 inline-flex h-2.5 w-2.5 flex-shrink-0 rounded-full" x-class="n.unread ? 'bg-blue-500' : 'bg-transparent'"></span>
139139 <div class="flex-1 min-w-0">
140 <p class="font-medium text-gray-900 text-sm" :text="labelFor(n)"></p>
141 <p class="mt-1 text-gray-500 text-xs" :text="detailFor(n)"></p>
142 <p class="mt-1 text-gray-400 text-xs" :text="relativeTime(n.created_at)"></p>
140 <p class="font-medium text-gray-900 text-sm">{{ labelFor(n) }}</p>
141 <p class="mt-1 text-gray-500 text-xs">{{ detailFor(n) }}</p>
142 <p class="mt-1 text-gray-400 text-xs">{{ relativeTime(n.created_at) }}</p>
143143 </div>
144144 </button>
145145 </li>
resources/components/Bench/PricingSection.stxmodified+3-3
Changes to resources/components/Bench/PricingSection.stx
@@ -43,7 +43,7 @@
4343 <p class="mt-4 text-gray-600 text-sm/6">Are you a clerk? This is for you.</p>
4444 <p class="flex gap-x-1 items-baseline mt-6">
4545 <span class="font-semibold text-4xl text-gray-800 tracking-tight">$0</span>
46 <span class="font-semibold text-gray-600 text-sm/6">/<span x-text="pricingPeriod() === 'monthly' ? 'month' : 'year'"></span></span>
46 <span class="font-semibold text-gray-600 text-sm/6">/<span>{{ pricingPeriod() === 'monthly' ? 'month' : 'year' }}</span></span>
4747 </p>
4848 <StxLink to="/register" aria-describedby="tier-free"
4949 class="block mt-6 px-2.5 py-1.5 font-semibold text-center text-gray-600 text-sm/6 ring-1 ring-gray-200 ring-inset rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 hover:ring-gray-300">Get
@@ -63,8 +63,8 @@
6363 <h3 id="tier-hobby" class="font-semibold text-gray-600 text-lg/8">Professional</h3>
6464 <p class="mt-4 text-gray-600 text-sm/6">For anyone curious about judge reviews.</p>
6565 <p class="flex gap-x-1 items-baseline mt-6">
66 <span class="font-semibold text-4xl text-gray-800 tracking-tight">$<span x-text="pricingPeriod() === 'monthly' ? '10' : '60'"></span></span>
67 <span class="font-semibold text-gray-600 text-sm/6">/<span x-text="pricingPeriod() === 'monthly' ? 'month' : 'year'"></span></span>
66 <span class="font-semibold text-4xl text-gray-800 tracking-tight">$<span>{{ pricingPeriod() === 'monthly' ? '10' : '60' }}</span></span>
67 <span class="font-semibold text-gray-600 text-sm/6">/<span>{{ pricingPeriod() === 'monthly' ? 'month' : 'year' }}</span></span>
6868 </p>
6969 <StxLink to="/register" aria-describedby="tier-hobby"
7070 class="block mt-6 px-2.5 py-1.5 font-semibold text-center text-sm/6 text-white bg-gray-600 hover:bg-gray-500 rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 shadow-sm">Get
resources/components/Bench/Profile/ProfileView.stxmodified+13-13
Changes to resources/components/Bench/Profile/ProfileView.stx
@@ -100,9 +100,9 @@
100100 <div class="flex gap-4 items-center">
101101 <img class="object-cover h-24 w-24 ring-2 ring-white/20 rounded-full" x-src="avatarUrl" x-alt="displayName" />
102102 <div>
103 <h1 class="font-bold text-2xl text-white" :text="displayName"></h1>
104 <p class="text-gray-400 text-sm" :text="userEmail"></p>
105 <p class="mt-1 text-gray-400 text-xs" :text="memberSince"></p>
103 <h1 class="font-bold text-2xl text-white">{{ displayName }}</h1>
104 <p class="text-gray-400 text-sm">{{ userEmail }}</p>
105 <p class="mt-1 text-gray-400 text-xs">{{ memberSince }}</p>
106106 </div>
107107 </div>
108108 <div class="flex flex-wrap gap-2">
@@ -117,19 +117,19 @@
117117 <div class="grid gap-4 grid-cols-2 mt-8 lg:grid-cols-4">
118118 <div class="p-6 bg-white border border-gray-200 rounded-xl">
119119 <p class="text-gray-500 text-sm">Total Reviews</p>
120 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="profileStore.totalReviews"></p>
120 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ profileStore.totalReviews }}</p>
121121 </div>
122122 <div class="p-6 bg-white border border-gray-200 rounded-xl">
123123 <p class="text-gray-500 text-sm">Judges Reviewed</p>
124 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="profileStore.judgesReviewed"></p>
124 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ profileStore.judgesReviewed }}</p>
125125 </div>
126126 <div class="p-6 bg-white border border-gray-200 rounded-xl">
127127 <p class="text-gray-500 text-sm">Average Rating</p>
128 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="profileStore.averageRating || '—'"></p>
128 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ profileStore.averageRating || '—' }}</p>
129129 </div>
130130 <div class="p-6 bg-white border border-gray-200 rounded-xl">
131131 <p class="text-gray-500 text-sm">Total Likes</p>
132 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="profileStore.totalLikes"></p>
132 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ profileStore.totalLikes }}</p>
133133 </div>
134134 </div>
135135
@@ -163,15 +163,15 @@
163163 <div class="flex-none h-2 w-2 mt-2 bg-gray-900 rounded-full"></div>
164164 <div class="flex-1 min-w-0">
165165 <div class="flex flex-wrap items-baseline gap-x-2">
166 <StxLink x-to="`/article/${review.id}`" class="font-medium text-gray-900 text-sm hover:underline" :text="review.title"></StxLink>
166 <StxLink x-to="`/article/${review.id}`" class="font-medium text-gray-900 text-sm hover:underline">{{ review.title }}</StxLink>
167167 <span :if="review.judge" class="text-gray-500 text-xs">
168168 on
169 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="hover:underline" :text="review.judge.name"></StxLink>
169 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="hover:underline">{{ review.judge.name }}</StxLink>
170170 </span>
171171 </div>
172 <p class="mt-0.5 text-gray-500 text-sm" :text="snippet(review.content)"></p>
172 <p class="mt-0.5 text-gray-500 text-sm">{{ snippet(review.content) }}</p>
173173 <div class="flex items-center gap-3 mt-1">
174 <span class="text-gray-400 text-xs" :text="relativeTime(review.created_at)"></span>
174 <span class="text-gray-400 text-xs">{{ relativeTime(review.created_at) }}</span>
175175 <span :if="review.status === 'pending'" class="px-1.5 py-0.5 font-medium text-yellow-700 text-xs bg-yellow-50 ring-1 ring-yellow-200 rounded">Pending review</span>
176176 </div>
177177 </div>
@@ -186,8 +186,8 @@
186186 <li :for="j in suggestedJudges" class="flex gap-3 items-center">
187187 <img class="object-cover flex-none h-10 w-10 rounded-full grayscale filter" x-src="j.image_url || j.photo" x-alt="j.name" />
188188 <div class="flex-1 min-w-0">
189 <p class="font-medium text-gray-900 text-sm truncate" :text="j.name"></p>
190 <p class="text-gray-500 text-xs truncate" :text="(j.court && j.court.name) || j.court || ''"></p>
189 <p class="font-medium text-gray-900 text-sm truncate">{{ j.name }}</p>
190 <p class="text-gray-500 text-xs truncate">{{ (j.court && j.court.name) || j.court || '' }}</p>
191191 </div>
192192 <StxLink x-to="`/review/${j.id}`" class="flex-none px-2.5 py-1 font-medium text-gray-900 text-xs bg-gray-100 hover:bg-gray-200 rounded-md transition-colors">Review</StxLink>
193193 </li>
resources/components/Bench/ReviewForm.stxmodified+13-17
Changes to resources/components/Bench/ReviewForm.stx
@@ -375,7 +375,7 @@
375375 </div>
376376 <h2 class="mt-5 font-semibold text-xl text-gray-900">Thanks — your review is in.</h2>
377377 <p class="mt-2 leading-7 text-gray-600 text-sm max-w-md mx-auto">
378 Your review of <span class="font-medium text-gray-900" :text="submitted().judgeName"></span>
378 Your review of <span class="font-medium text-gray-900">{{ submitted().judgeName }}</span>
379379 is pending moderation. We typically publish within a day. You can see your reviews any time
380380 from your profile.
381381 </p>
@@ -393,7 +393,7 @@
393393 <div :if="submitted().reviewId" class="mt-8 pt-6 border-t border-gray-100 text-left max-w-md mx-auto">
394394 <div class="flex items-baseline justify-between mb-3">
395395 <h3 class="font-semibold text-gray-900 text-sm">Attach photos (optional)</h3>
396 <span class="text-gray-500 text-xs" :text="`${photoCount} / 4 attached`"></span>
396 <span class="text-gray-500 text-xs">{{ `${photoCount} / 4 attached` }}</span>
397397 </div>
398398 <p class="mb-3 text-gray-500 text-xs leading-relaxed">JPEG, PNG, or WebP up to 8MB each. Camera metadata (including GPS) is stripped at upload — your photos won't reveal where you were when you took them.</p>
399399
@@ -418,12 +418,10 @@
418418 <div
419419 role="alert"
420420 class="mt-3 px-3 py-2 text-red-800 text-xs bg-red-50 border border-red-200 rounded-md"
421 x-class="photoError() ? '' : 'hidden'"
422 :text="photoError()"
423 ></div>
421 x-class="photoError() ? '' : 'hidden'">{{ photoError() }}</div>
424422
425423 <p :if="photoUploading" class="mt-3 text-gray-500 text-xs">Uploading and resizing — this can take a few seconds per photo.</p>
426 <p :if="!photoUploading() && photoCount() > 0" class="mt-3 text-emerald-700 text-xs" :text="`${photoCount} ${photoCount() === 1 ? 'photo' : 'photos'} attached. You can add more or close this page — they'll appear on your review once moderation publishes it.`"></p>
424 <p :if="!photoUploading() && photoCount() > 0" class="mt-3 text-emerald-700 text-xs">{{ `${photoCount} ${photoCount() === 1 ? 'photo' : 'photos'} attached. You can add more or close this page — they'll appear on your review once moderation publishes it.` }}</p>
427425 </div>
428426 </div>
429427
@@ -441,9 +439,9 @@
441439 <div class="flex gap-4 items-center">
442440 <img class="object-cover h-16 w-16 ring-2 ring-gray-100 rounded-full grayscale filter" x-src="judge().photo" x-alt="judge().name" />
443441 <div class="min-w-0">
444 <h3 class="font-semibold text-gray-900 text-lg" :text="judge().name"></h3>
445 <p class="text-gray-500 text-sm" :text="judge().court"></p>
446 <p :if="judge().department" class="text-gray-400 text-xs" :text="judge().department"></p>
442 <h3 class="font-semibold text-gray-900 text-lg">{{ judge().name }}</h3>
443 <p class="text-gray-500 text-sm">{{ judge().court }}</p>
444 <p :if="judge().department" class="text-gray-400 text-xs">{{ judge().department }}</p>
447445 </div>
448446 </div>
449447 <button
@@ -500,7 +498,7 @@
500498 <Icon name="star" size="24" />
501499 </button>
502500 </div>
503 <span class="ml-2 w-24 text-gray-500 text-sm" :text="fairness() ? getRatingText(fairness()) : ''"></span>
501 <span class="ml-2 w-24 text-gray-500 text-sm">{{ fairness() ? getRatingText(fairness()) : '' }}</span>
504502 </div>
505503 </div>
506504
@@ -521,7 +519,7 @@
521519 <Icon name="star" size="24" />
522520 </button>
523521 </div>
524 <span class="ml-2 w-24 text-gray-500 text-sm" :text="knowledge() ? getRatingText(knowledge()) : ''"></span>
522 <span class="ml-2 w-24 text-gray-500 text-sm">{{ knowledge() ? getRatingText(knowledge()) : '' }}</span>
525523 </div>
526524 </div>
527525
@@ -542,7 +540,7 @@
542540 <Icon name="star" size="24" />
543541 </button>
544542 </div>
545 <span class="ml-2 w-24 text-gray-500 text-sm" :text="demeanor() ? getRatingText(demeanor()) : ''"></span>
543 <span class="ml-2 w-24 text-gray-500 text-sm">{{ demeanor() ? getRatingText(demeanor()) : '' }}</span>
546544 </div>
547545 </div>
548546 </div>
@@ -582,7 +580,7 @@
582580 <Icon name="star" size="32" />
583581 </button>
584582 </div>
585 <span class="ml-3 font-medium text-gray-700 text-sm" :text="ratingText()"></span>
583 <span class="ml-3 font-medium text-gray-700 text-sm">{{ ratingText() }}</span>
586584 </div>
587585 </div>
588586
@@ -616,9 +614,7 @@
616614 role="alert"
617615 aria-live="polite"
618616 class="mb-4 p-3 text-red-700 text-sm bg-red-50 border border-red-200 rounded-md"
619 x-class="submitError() ? '' : 'hidden'"
620 :text="submitError()"
621 ></div>
617 x-class="submitError() ? '' : 'hidden'">{{ submitError() }}</div>
622618
623619 <!-- Anonymity toggle (bench-review#36). Default OFF — anonymity
624620 is an explicit opt-in. Surfaces above the action bar so the
@@ -663,7 +659,7 @@
663659 ? 'bg-gray-900 hover:bg-gray-700'
664660 : 'bg-gray-300 cursor-not-allowed'
665661 ]"
666 ><svg :if="reviewsStore.submitting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span :text="reviewsStore.submitting() ? 'Submitting…' : 'Submit Review'"></span></button>
662 ><svg :if="reviewsStore.submitting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span>{{ reviewsStore.submitting() ? 'Submitting…' : 'Submit Review' }}</span></button>
667663 </div>
668664 </div>
669665
resources/components/Bench/ReviewsFeed.stxmodified+8-10
Changes to resources/components/Bench/ReviewsFeed.stx
@@ -208,9 +208,7 @@
208208 <div
209209 role="alert"
210210 class="mb-4 px-3 py-2 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
211 x-class="likeError() ? '' : 'hidden'"
212 :text="likeError()"
213 ></div>
211 x-class="likeError() ? '' : 'hidden'">{{ likeError() }}</div>
214212 <!-- Loading skeleton (bench-review#35). Renders three placeholder
215213 review cards matching the eventual layout (avatar circle + name
216214 line + meta line + 3-line body + footer chip row) so the feed
@@ -275,25 +273,25 @@
275273 </StxLink>
276274 <div class="ml-2">
277275 <p class="font-medium text-gray-900 text-sm">
278 <span :if="review.author.id === 0" :text="review.author.name"></span>
279 <StxLink :if="review.author.id !== 0" x-to="`/user/${review.author.id}`" class="hover:underline" :text="review.author.name"></StxLink>
276 <span :if="review.author.id === 0">{{ review.author.name }}</span>
277 <StxLink :if="review.author.id !== 0" x-to="`/user/${review.author.id}`" class="hover:underline">{{ review.author.name }}</StxLink>
280278 </p>
281279 <p class="text-gray-500 text-xs">reviewed</p>
282280 </div>
283281 </div>
284 <span class="text-gray-600 text-sm" :text="review.date"></span>
282 <span class="text-gray-600 text-sm">{{ review.date }}</span>
285283 </div>
286284
287285 <!-- Title + content -->
288286 <div class="mt-3">
289 <h3 class="mb-2 font-bold text-gray-900 text-xl" :text="review.title"></h3>
287 <h3 class="mb-2 font-bold text-gray-900 text-xl">{{ review.title }}</h3>
290288 <p class="leading-relaxed line-clamp-3 text-base text-gray-700" x-html="review.content"></p>
291289 </div>
292290
293291 <!-- Judge name + rating + actions -->
294292 <div class="flex items-center justify-between mt-4">
295293 <div class="flex items-center space-x-3">
296 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="font-semibold text-base text-gray-900 hover:underline" :text="review.judge.name"></StxLink>
294 <StxLink x-to="`/judges/${review.judge.id}/profile`" class="font-semibold text-base text-gray-900 hover:underline">{{ review.judge.name }}</StxLink>
297295 <div class="flex items-center">
298296 <Icon :for="star in stars" name="star" size="16" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-300'" />
299297 </div>
@@ -314,7 +312,7 @@
314312 <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="mr-1.5 transition-colors">
315313 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
316314 </svg>
317 <span class="tabular-nums" :text="review.likes"></span>
315 <span class="tabular-nums">{{ review.likes }}</span>
318316 </button>
319317 <!-- Author: read-only count. Still shows the reaction
320318 total — it's useful feedback for the writer — but no
@@ -328,7 +326,7 @@
328326 <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="mr-1.5">
329327 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
330328 </svg>
331 <span class="tabular-nums" :text="review.likes"></span>
329 <span class="tabular-nums">{{ review.likes }}</span>
332330 </span>
333331 <StxLink x-to="`/article/${review.id}`" class="text-gray-600 text-sm hover:text-gray-500 hover:underline">
334332 Read more
resources/components/Bench/SearchView.stxmodified+11-11
Changes to resources/components/Bench/SearchView.stx
@@ -91,10 +91,10 @@
9191 <div class="flex items-center justify-between mt-6">
9292 <div>
9393 <h1 class="font-semibold text-2xl text-gray-900">
94 <span :if="search.query" :text="`Results for &quot;${search.query}&quot;`"></span>
94 <span :if="search.query">{{ `Results for &quot;${search.query}&quot;` }}</span>
9595 <span :if="!search.query">All results</span>
9696 </h1>
97 <p class="mt-1 text-gray-600 text-sm" :text="totalResults + ' results found'"></p>
97 <p class="mt-1 text-gray-600 text-sm">{{ totalResults + ' results found' }}</p>
9898 </div>
9999 </div>
100100
@@ -106,7 +106,7 @@
106106 class="px-1 py-3 font-medium text-sm whitespace-nowrap border-b-2"
107107 x-class="search.tab === 'all' ? 'border-gray-600 text-gray-900' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'"
108108 >
109 All <span class="ml-1 text-gray-400" :text="`(${totalResults})`"></span>
109 All <span class="ml-1 text-gray-400">{{ `(${totalResults})` }}</span>
110110 </button>
111111 <button
112112 type="button"
@@ -114,7 +114,7 @@
114114 class="px-1 py-3 font-medium text-sm whitespace-nowrap border-b-2"
115115 x-class="search.tab === 'judges' ? 'border-gray-600 text-gray-900' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'"
116116 >
117 Judges <span class="ml-1 text-gray-400" :text="`(${filteredJudges.length})`"></span>
117 Judges <span class="ml-1 text-gray-400">{{ `(${filteredJudges.length})` }}</span>
118118 </button>
119119 <button
120120 type="button"
@@ -122,7 +122,7 @@
122122 class="px-1 py-3 font-medium text-sm whitespace-nowrap border-b-2"
123123 x-class="search.tab === 'courts' ? 'border-gray-600 text-gray-900' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'"
124124 >
125 Courts <span class="ml-1 text-gray-400" :text="`(${filteredCourts.length})`"></span>
125 Courts <span class="ml-1 text-gray-400">{{ `(${filteredCourts.length})` }}</span>
126126 </button>
127127 </nav>
128128 </div>
@@ -133,7 +133,7 @@
133133 <svg class="mx-auto h-12 w-12 text-gray-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
134134 <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
135135 </svg>
136 <p class="mt-4 font-medium text-gray-900" :text="`No results for &quot;${search.query}&quot;`"></p>
136 <p class="mt-4 font-medium text-gray-900">{{ `No results for &quot;${search.query}&quot;` }}</p>
137137 <p class="mt-1 text-gray-500 text-sm">Try a different name, court, or location.</p>
138138 <div class="flex gap-3 items-center justify-center mt-5">
139139 <StxLink to="/judges" class="inline-flex items-center px-4 py-2 font-medium text-white text-sm bg-gray-800 rounded-lg hover:bg-gray-700 transition-colors">Browse judges</StxLink>
@@ -155,13 +155,13 @@
155155 <StxLink x-to="`/judges/${judge.id}/profile`" class="flex flex-1 gap-x-4 items-center min-w-0">
156156 <img class="object-cover flex-none h-12 w-12 rounded-full grayscale filter" x-src="judge.photo" x-alt="judge.name">
157157 <div class="flex-auto min-w-0">
158 <p class="font-semibold text-gray-900 text-sm hover:underline" :text="judge.name"></p>
159 <p class="mt-0.5 text-gray-500 text-xs" :text="`${judge.court.name} · ${judge.location}`"></p>
158 <p class="font-semibold text-gray-900 text-sm hover:underline">{{ judge.name }}</p>
159 <p class="mt-0.5 text-gray-500 text-xs">{{ `${judge.court.name} · ${judge.location}` }}</p>
160160 </div>
161161 </StxLink>
162162 <div class="flex gap-x-2 items-center">
163163 <Icon :for="star in stars" name="star" size="16" x-class="star <= judge.rating ? 'text-yellow-400' : 'text-gray-200'" />
164 <span class="ml-1 text-gray-500 text-xs" :text="`(${judge.reviewCount})`"></span>
164 <span class="ml-1 text-gray-500 text-xs">{{ `(${judge.reviewCount})` }}</span>
165165 </div>
166166 </li>
167167 </ul>
@@ -187,8 +187,8 @@
187187 <div class="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent"></div>
188188 </div>
189189 <div class="p-4">
190 <h3 class="font-semibold text-gray-900 text-sm group-hover:text-deep-brown" :text="court.name"></h3>
191 <p class="mt-1 text-gray-500 text-xs" :text="`${court.city}, ${court.state}`"></p>
190 <h3 class="font-semibold text-gray-900 text-sm group-hover:text-deep-brown">{{ court.name }}</h3>
191 <p class="mt-1 text-gray-500 text-xs">{{ `${court.city}, ${court.state}` }}</p>
192192 </div>
193193 </StxLink>
194194 </div>
resources/components/Bench/Settings/SettingsView.stxmodified+11-17
Changes to resources/components/Bench/Settings/SettingsView.stx
@@ -320,7 +320,7 @@
320320 x-class="photoUploading() ? 'opacity-60 pointer-events-none' : ''"
321321 >
322322 <svg :if="photoUploading()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
323 <span :text="photoUploading() ? 'Uploading…' : 'Change photo'"></span>
323 <span>{{ photoUploading() ? 'Uploading…' : 'Change photo' }}</span>
324324 <input
325325 type="file"
326326 accept="image/jpeg,image/png,image/webp"
@@ -333,9 +333,7 @@
333333 <div
334334 role="alert"
335335 class="mt-2 text-red-600 text-xs"
336 x-class="photoError() ? '' : 'hidden'"
337 :text="photoError()"
338 ></div>
336 x-class="photoError() ? '' : 'hidden'">{{ photoError() }}</div>
339337 </div>
340338
341339 <div class="grid gap-x-6 gap-y-5 grid-cols-1 sm:grid-cols-2">
@@ -369,7 +367,7 @@
369367 <div class="flex items-center justify-between gap-x-4 pt-2">
370368 <div>
371369 <p :if="saveStatus === 'saved'" class="text-emerald-700 text-sm">Saved.</p>
372 <p :if="saveStatus === 'error'" class="text-red-600 text-sm" :text="saveError"></p>
370 <p :if="saveStatus === 'error'" class="text-red-600 text-sm">{{ saveError }}</p>
373371 </div>
374372 <button
375373 type="button"
@@ -378,7 +376,7 @@
378376 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-sm text-white bg-gray-900 hover:bg-gray-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-md transition-colors shadow-sm"
379377 >
380378 <svg :if="saveStatus() === 'saving'" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
381 <span :text="saveStatus === 'saving' ? 'Saving…' : 'Save changes'"></span>
379 <span>{{ saveStatus === 'saving' ? 'Saving…' : 'Save changes' }}</span>
382380 </button>
383381 </div>
384382 </div>
@@ -435,7 +433,7 @@
435433 <div class="flex items-center justify-between gap-x-4 pt-2">
436434 <div>
437435 <p :if="pwStatus === 'saved'" class="text-emerald-700 text-sm">Password updated.</p>
438 <p :if="pwStatus === 'error'" class="text-red-600 text-sm" :text="pwError"></p>
436 <p :if="pwStatus === 'error'" class="text-red-600 text-sm">{{ pwError }}</p>
439437 </div>
440438 <button
441439 type="button"
@@ -444,7 +442,7 @@
444442 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-sm text-white bg-gray-900 hover:bg-gray-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-md transition-colors shadow-sm"
445443 >
446444 <svg :if="pwStatus() === 'saving'" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
447 <span :text="pwStatus === 'saving' ? 'Updating…' : 'Update password'"></span>
445 <span>{{ pwStatus === 'saving' ? 'Updating…' : 'Update password' }}</span>
448446 </button>
449447 </div>
450448 </div>
@@ -499,9 +497,7 @@
499497 <div
500498 role="alert"
501499 class="px-3 py-2 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
502 x-class="credentialStatus === 'error' ? '' : 'hidden'"
503 :text="credentialError()"
504 ></div>
500 x-class="credentialStatus === 'error' ? '' : 'hidden'">{{ credentialError() }}</div>
505501
506502 <div
507503 role="status"
@@ -517,7 +513,7 @@
517513 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
518514 >
519515 <svg :if="credentialStatus() === 'saving'" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
520 <span :text="credentialStatus === 'saving' ? 'Submitting…' : 'Submit claim for verification'"></span>
516 <span>{{ credentialStatus === 'saving' ? 'Submitting…' : 'Submit claim for verification' }}</span>
521517 </button>
522518 </div>
523519 </div>
@@ -550,9 +546,7 @@
550546 type="button"
551547 @click="exportData()"
552548 x-disabled="exporting"
553 class="px-3 py-1.5 font-medium text-gray-700 text-sm bg-white hover:bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-md transition-colors disabled:opacity-50"
554 :text="exporting() ? 'Preparing…' : 'Export data'"
555 ></button>
549 class="px-3 py-1.5 font-medium text-gray-700 text-sm bg-white hover:bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-md transition-colors disabled:opacity-50">{{ exporting() ? 'Preparing…' : 'Export data' }}</button>
556550 </div>
557551
558552 <!-- Delete account -->
@@ -580,7 +574,7 @@
580574 autocomplete="current-password"
581575 class="block mt-2 px-3 py-2 w-full max-w-sm text-gray-900 text-sm bg-white border border-gray-300 rounded-md focus:border-red-500 focus:outline-none focus:ring-2 focus:ring-red-200"
582576 >
583 <p role="alert" class="mt-2 text-red-700 text-xs" x-class="deleteError() ? '' : 'hidden'" :text="deleteError()"></p>
577 <p role="alert" class="mt-2 text-red-700 text-xs" x-class="deleteError() ? '' : 'hidden'">{{ deleteError() }}</p>
584578 <div class="flex gap-x-3 mt-3">
585579 <button
586580 type="button"
@@ -589,7 +583,7 @@
589583 class="inline-flex items-center justify-center px-4 py-2 font-semibold text-white text-sm bg-red-600 hover:bg-red-700 rounded-md disabled:opacity-50"
590584 >
591585 <svg :if="deleting()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
592 <span :text="deleting() ? 'Deleting…' : 'Permanently delete'"></span>
586 <span>{{ deleting() ? 'Deleting…' : 'Permanently delete' }}</span>
593587 </button>
594588 <button
595589 type="button"
resources/components/Bench/ToastHost.stxmodified+2-2
Changes to resources/components/Bench/ToastHost.stx
@@ -17,8 +17,8 @@
1717 x-class="t.type === 'error' ? 'bg-red-50 border-red-200 text-red-800' : t.type === 'success' ? 'bg-green-50 border-green-200 text-green-800' : t.type === 'warning' ? 'bg-amber-50 border-amber-200 text-amber-900' : 'bg-white border-gray-200 text-gray-800'"
1818 >
1919 <div class="flex-1 min-w-0">
20 <p :if="t.title" class="font-semibold text-sm" :text="t.title"></p>
21 <p class="text-sm break-words" :text="t.message"></p>
20 <p :if="t.title" class="font-semibold text-sm">{{ t.title }}</p>
21 <p class="text-sm break-words">{{ t.message }}</p>
2222 </div>
2323 <span class="flex-shrink-0 text-lg leading-none opacity-40" aria-hidden="true">&times;</span>
2424 </div>
resources/components/Bench/User/ReviewerProfile.stxmodified+17-19
Changes to resources/components/Bench/User/ReviewerProfile.stx
@@ -189,10 +189,10 @@
189189 <div class="p-10">
190190 <div class="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
191191 <div class="flex gap-4 items-center">
192 <span class="inline-flex items-center justify-center h-20 w-20 font-semibold text-amber-700 text-xl bg-amber-50 ring-2 ring-white/20 rounded-full" :text="initials"></span>
192 <span class="inline-flex items-center justify-center h-20 w-20 font-semibold text-amber-700 text-xl bg-amber-50 ring-2 ring-white/20 rounded-full">{{ initials }}</span>
193193 <div>
194194 <div class="flex flex-wrap items-center gap-2">
195 <h1 class="font-bold text-2xl text-white" :text="displayName"></h1>
195 <h1 class="font-bold text-2xl text-white">{{ displayName }}</h1>
196196 <!-- Verified-credential badge (bench-review#37). Emerald
197197 check pill, only rendered when the API surfaced a
198198 non-null credential block (admin-approved). -->
@@ -207,9 +207,9 @@
207207 Verified
208208 </span>
209209 </div>
210 <p :if="memberSince" class="mt-1 text-gray-400 text-sm" :text="`Member since ${memberSince}`"></p>
211 <p :if="verifiedCredential" class="text-gray-400 text-xs" :text="credentialLabel"></p>
212 <p class="mt-1 text-gray-400 text-xs" :text="reviewCountLabel"></p>
210 <p :if="memberSince" class="mt-1 text-gray-400 text-sm">{{ `Member since ${memberSince}` }}</p>
211 <p :if="verifiedCredential" class="text-gray-400 text-xs">{{ credentialLabel }}</p>
212 <p class="mt-1 text-gray-400 text-xs">{{ reviewCountLabel }}</p>
213213 </div>
214214 </div>
215215 <div class="flex flex-wrap gap-2">
@@ -232,25 +232,25 @@
232232 <div class="grid gap-4 grid-cols-2 mt-8 lg:grid-cols-4">
233233 <div class="p-6 bg-white border border-gray-200 rounded-xl">
234234 <p class="text-gray-500 text-sm">Reviews</p>
235 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="user()?.review_count ?? 0"></p>
235 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ user()?.review_count ?? 0 }}</p>
236236 </div>
237237 <div class="p-6 bg-white border border-gray-200 rounded-xl">
238238 <p class="text-gray-500 text-sm">Judges Reviewed</p>
239 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="user()?.judges_reviewed ?? 0"></p>
239 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ user()?.judges_reviewed ?? 0 }}</p>
240240 </div>
241241 <div class="p-6 bg-white border border-gray-200 rounded-xl">
242242 <p class="text-gray-500 text-sm">Average Rating</p>
243 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="user()?.avg_rating == null ? '—' : user().avg_rating"></p>
243 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ user()?.avg_rating == null ? '—' : user().avg_rating }}</p>
244244 </div>
245245 <div class="p-6 bg-white border border-gray-200 rounded-xl">
246246 <p class="text-gray-500 text-sm">Total Likes</p>
247 <p class="mt-1 font-semibold text-3xl text-gray-900" :text="user()?.total_likes ?? 0"></p>
247 <p class="mt-1 font-semibold text-3xl text-gray-900">{{ user()?.total_likes ?? 0 }}</p>
248248 </div>
249249 </div>
250250
251251 <!-- Reviews list -->
252252 <div class="mt-10">
253 <h2 class="font-semibold text-base text-gray-900" :text="`${displayName}'s published reviews`"></h2>
253 <h2 class="font-semibold text-base text-gray-900">{{ `${displayName}'s published reviews` }}</h2>
254254
255255 <!-- Loading skeleton -->
256256 <ul :if="!reviewsLoaded" class="mt-6 space-y-4">
@@ -269,7 +269,7 @@
269269 <!-- Empty state -->
270270 <div :if="reviewsLoaded && reviews.length === 0" class="mt-6 p-10 text-center bg-white border border-gray-200 rounded-lg">
271271 <p class="font-semibold text-gray-900 text-base">No published reviews yet.</p>
272 <p class="mt-2 text-gray-500 text-sm" :text="isOwnProfile() ? 'When a moderator approves your first review, it lands here.' : 'This reviewer hasn\\'t had a review published yet.'"></p>
272 <p class="mt-2 text-gray-500 text-sm">{{ isOwnProfile() ? 'When a moderator approves your first review, it lands here.' : 'This reviewer hasn\\'t had a review published yet.' }}</p>
273273 </div>
274274
275275 <!-- Reviews -->
@@ -278,22 +278,20 @@
278278 <div class="flex flex-wrap items-baseline gap-x-3 gap-y-1">
279279 <StxLink
280280 x-to="`/article/${r.id}`"
281 class="font-semibold text-gray-900 text-base hover:underline underline-offset-2 decoration-gray-300"
282 :text="r.title"
283 ></StxLink>
281 class="font-semibold text-gray-900 text-base hover:underline underline-offset-2 decoration-gray-300">{{ r.title }}</StxLink>
284282 <span :if="r.judge" class="text-gray-500 text-xs">
285283 on
286 <StxLink x-to="`/judges/${r.judge.id}/profile`" class="text-gray-700 hover:underline underline-offset-2" :text="r.judge.name"></StxLink>
284 <StxLink x-to="`/judges/${r.judge.id}/profile`" class="text-gray-700 hover:underline underline-offset-2">{{ r.judge.name }}</StxLink>
287285 </span>
288286 </div>
289287 <div class="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-gray-500 text-xs">
290 <span :text="formatDate(r.created_at)"></span>
288 <span>{{ formatDate(r.created_at) }}</span>
291289 <span class="text-gray-300">·</span>
292 <span class="inline-flex items-center gap-1 tabular-nums" :text="`${r.rating}/5`"></span>
290 <span class="inline-flex items-center gap-1 tabular-nums">{{ `${r.rating}/5` }}</span>
293291 <span :if="r.likes && r.likes > 0" class="text-gray-300">·</span>
294 <span :if="r.likes && r.likes > 0" class="inline-flex items-center gap-1" :text="`${r.likes} ${r.likes === 1 ? 'reaction' : 'reactions'}`"></span>
292 <span :if="r.likes && r.likes > 0" class="inline-flex items-center gap-1">{{ `${r.likes} ${r.likes === 1 ? 'reaction' : 'reactions'}` }}</span>
295293 </div>
296 <p class="mt-3 text-gray-600 text-sm line-clamp-2" :text="snippet(r.content)"></p>
294 <p class="mt-3 text-gray-600 text-sm line-clamp-2">{{ snippet(r.content) }}</p>
297295 <div class="flex flex-wrap items-center gap-x-4 mt-4 pt-3 border-t border-gray-100 text-sm">
298296 <StxLink x-to="`/article/${r.id}`" class="font-medium text-gray-900 hover:underline underline-offset-2">
299297 Read review →
resources/components/Bench/VerifyEmail.stxmodified+5-9
Changes to resources/components/Bench/VerifyEmail.stx
@@ -97,15 +97,13 @@
9797 <!-- Error (bad/expired link) -->
9898 <div x-class="phase() === 'error' ? '' : 'hidden'">
9999 <h1 class="font-serif font-bold text-2xl text-gray-900">Verification failed</h1>
100 <p class="mt-2 text-gray-600 text-sm" :text="message()"></p>
100 <p class="mt-2 text-gray-600 text-sm">{{ message() }}</p>
101101 <button
102102 type="button"
103103 @click="resend()"
104104 x-disabled="resending"
105 class="mt-5 inline-flex items-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50"
106 :text="resending() ? 'Sending…' : 'Resend verification email'"
107 ></button>
108 <p class="mt-3 text-gray-500 text-xs" :text="resendNote()"></p>
105 class="mt-5 inline-flex items-center px-4 py-2 font-semibold text-white text-sm bg-gray-900 hover:bg-gray-800 rounded-md disabled:opacity-50">{{ resending() ? 'Sending…' : 'Resend verification email' }}</button>
106 <p class="mt-3 text-gray-500 text-xs">{{ resendNote() }}</p>
109107 </div>
110108
111109 <!-- Landing (arrived at /verify-email with no token) -->
@@ -116,10 +114,8 @@
116114 type="button"
117115 @click="resend()"
118116 x-disabled="resending"
119 class="mt-5 inline-flex items-center px-4 py-2 font-semibold text-gray-800 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50"
120 :text="resending() ? 'Sending…' : 'Resend verification email'"
121 ></button>
122 <p class="mt-3 text-gray-500 text-xs" :text="resendNote()"></p>
117 class="mt-5 inline-flex items-center px-4 py-2 font-semibold text-gray-800 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50">{{ resending() ? 'Sending…' : 'Resend verification email' }}</button>
118 <p class="mt-3 text-gray-500 text-xs">{{ resendNote() }}</p>
123119 </div>
124120 </div>
125121</div>
resources/views/admin/login.stxmodified+2-5
Changes to resources/views/admin/login.stx
@@ -86,16 +86,13 @@
8686 role="alert"
8787 aria-live="polite"
8888 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
89 x-class="submitError() ? '' : 'hidden'"
90 :text="submitError()"
91 ></div>
89 x-class="submitError() ? '' : 'hidden'">{{ submitError() }}</div>
9290
9391 <button
9492 type="submit"
9593 x-disabled="loading"
9694 class="flex justify-center px-3 py-1.5 w-full font-semibold text-sm/6 text-white bg-gray-900 hover:bg-gray-800 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-900 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
97 :text="loading() ? 'Signing in...' : 'Sign in to admin'"
98 >Sign in to admin</button>
95 >{{ loading() ? 'Signing in...' : 'Sign in to admin' }}</button>
9996 </form>
10097 </div>
10198
resources/views/blog/index.stxmodified+4-4
Changes to resources/views/blog/index.stx
@@ -32,16 +32,16 @@
3232 </div>
3333 <div class="flex-1">
3434 <div class="flex gap-x-4 items-center text-xs">
35 <time :datetime="post.dateTime" class="text-gray-500" :text="post.date"></time>
35 <time :datetime="post.dateTime" class="text-gray-500">{{ post.date }}</time>
3636 </div>
3737 <div class="relative max-w-xl group">
38 <h3 class="mt-3 font-semibold leading-6 text-gray-900 text-lg group-hover:text-softBrown duration-200 transition-colors" :text="post.title"></h3>
38 <h3 class="mt-3 font-semibold leading-6 text-gray-900 text-lg group-hover:text-softBrown duration-200 transition-colors">{{ post.title }}</h3>
3939 <div class="flex gap-x-4 items-center mt-5">
4040 <div class="leading-6 text-sm">
41 <p class="font-semibold text-gray-900" :text="post.author.name"></p>
41 <p class="font-semibold text-gray-900">{{ post.author.name }}</p>
4242 </div>
4343 </div>
44 <p class="mt-5 leading-6 line-clamp-3 text-gray-600 text-sm" :text="post.content"></p>
44 <p class="mt-5 leading-6 line-clamp-3 text-gray-600 text-sm">{{ post.content }}</p>
4545 </div>
4646 </div>
4747 </article>
resources/views/contact.stxmodified+1-1
Changes to resources/views/contact.stx
@@ -148,7 +148,7 @@
148148 </div>
149149 </div>
150150
151 <p x-class="submitError() ? 'text-red-600 text-sm' : 'hidden'" :text="submitError()"></p>
151 <p x-class="submitError() ? 'text-red-600 text-sm' : 'hidden'">{{ submitError() }}</p>
152152
153153 <button
154154 type="submit"
resources/views/court-houses/[id].stxmodified+5-5
Changes to resources/views/court-houses/[id].stx
@@ -31,8 +31,8 @@
3131 <div class="flex gap-x-6 items-center">
3232 <img x-src="court.image" x-alt="court.name" class="object-cover flex-none h-16 w-16 ring-1 ring-gray-900/10 rounded-lg">
3333 <h1>
34 <div class="text-gray-500 text-sm/6">Court House <span class="text-gray-700" :text="`#${court.id}`"></span></div>
35 <div class="mt-1 font-semibold text-base text-gray-900" :text="court.name"></div>
34 <div class="text-gray-500 text-sm/6">Court House <span class="text-gray-700">{{ `#${court.id}` }}</span></div>
35 <div class="mt-1 font-semibold text-base text-gray-900">{{ court.name }}</div>
3636 </h1>
3737 </div>
3838 <div class="flex gap-x-4 items-center sm:gap-x-6">
@@ -91,15 +91,15 @@
9191 <div class="p-4">
9292 <dl class="flex flex-wrap">
9393 <div class="flex-auto">
94 <dt class="font-semibold text-gray-900 text-sm/6" :text="court.name"></dt>
95 <dd class="mt-1 text-gray-700 text-sm" :text="`${court.city}, ${court.state}`"></dd>
94 <dt class="font-semibold text-gray-900 text-sm/6">{{ court.name }}</dt>
95 <dd class="mt-1 text-gray-700 text-sm">{{ `${court.city}, ${court.state}` }}</dd>
9696 </div>
9797 <div class="flex flex-none gap-x-4 mt-6 pt-6 px-2 w-full border-gray-900/5 border-t">
9898 <dt class="flex-none">
9999 <span class="sr-only">Address</span>
100100 <Icon name="map-pin" size="20" class="text-gray-400" />
101101 </dt>
102 <dd class="text-gray-700 text-sm/6" :text="`${court.address} ${court.city}, ${court.state} ${court.zipCode}`"></dd>
102 <dd class="text-gray-700 text-sm/6">{{ `${court.address} ${court.city}, ${court.state} ${court.zipCode}` }}</dd>
103103 </div>
104104 <div class="flex flex-none gap-x-4 mt-4 px-2 w-full">
105105 <dt class="flex-none">
resources/views/faq.stxmodified+2-2
Changes to resources/views/faq.stx
@@ -67,14 +67,14 @@
6767 class="flex items-center justify-between w-full text-gray-900 text-left"
6868 @click="open.set(open() === idx ? -1 : idx)"
6969 >
70 <span class="font-semibold text-base" :text="faq.q"></span>
70 <span class="font-semibold text-base">{{ faq.q }}</span>
7171 <span class="flex items-center ml-6 h-7">
7272 <Icon name="chevron-down" size="24" class="duration-200 transition-transform" x-class="open() === idx ? 'rotate-180' : ''" />
7373 </span>
7474 </button>
7575 </dt>
7676 <dd :if="open() === idx" class="mt-4 pr-12">
77 <p class="leading-7 text-base text-gray-600" :text="faq.a"></p>
77 <p class="leading-7 text-base text-gray-600">{{ faq.a }}</p>
7878 </dd>
7979 </div>
8080 </dl>
resources/views/forgot-password.stxmodified+2-2
Changes to resources/views/forgot-password.stx
@@ -74,7 +74,7 @@
7474 </div>
7575 </div>
7676
77 <p :if="submitError()" class="text-red-600 text-sm" :text="submitError()"></p>
77 <p :if="submitError()" class="text-red-600 text-sm">{{ submitError() }}</p>
7878
7979 <button
8080 type="submit"
@@ -94,7 +94,7 @@
9494 </div>
9595 <h2 class="mt-4 font-semibold text-gray-900 text-lg">Check your email</h2>
9696 <p class="mt-2 text-gray-600 text-sm">
97 If an account exists for <span class="font-medium" :text="email"></span>, we've sent a password reset link.
97 If an account exists for <span class="font-medium">{{ email }}</span>, we've sent a password reset link.
9898 </p>
9999 </div>
100100
resources/views/home.stxmodified+20-20
Changes to resources/views/home.stx
@@ -181,7 +181,7 @@
181181 </form>
182182 <div class="flex flex-wrap gap-2 mt-4 text-gray-500 text-xs">
183183 <span class="font-medium">Popular:</span>
184 <StxLink :for="cat in popularCategories" x-to="`/search?type=${cat.name}`" class="hover:text-gray-700 hover:underline" :text="cat.name"></StxLink>
184 <StxLink :for="cat in popularCategories" x-to="`/search?type=${cat.name}`" class="hover:text-gray-700 hover:underline">{{ cat.name }}</StxLink>
185185 </div>
186186 </div>
187187 </div>
@@ -241,13 +241,13 @@
241241 <div class="flex gap-4 items-center">
242242 <img class="object-cover h-14 w-14 ring-2 ring-gray-50 rounded-full grayscale group-hover:grayscale-0 duration-200 transition-all" x-src="judge.image_url" x-alt="judge.name">
243243 <div class="min-w-0 flex-1">
244 <h3 class="font-semibold text-gray-900 truncate" :text="judge.name"></h3>
245 <p :if="judge.court_name" class="text-gray-500 text-sm truncate" :text="judge.court_name"></p>
244 <h3 class="font-semibold text-gray-900 truncate">{{ judge.name }}</h3>
245 <p :if="judge.court_name" class="text-gray-500 text-sm truncate">{{ judge.court_name }}</p>
246246 </div>
247247 </div>
248248 <div class="flex items-center justify-between mt-4 pt-3 border-t border-gray-100">
249 <span class="text-amber-700 text-xs font-medium" :text="`${judge.review_count} ${judge.review_count === 1 ? 'review' : 'reviews'} this week`"></span>
250 <span :if="judge.avg_rating" class="font-medium text-gray-700 text-xs tabular-nums" :text="`${judge.avg_rating} ★`"></span>
249 <span class="text-amber-700 text-xs font-medium">{{ `${judge.review_count} ${judge.review_count === 1 ? 'review' : 'reviews'} this week` }}</span>
250 <span :if="judge.avg_rating" class="font-medium text-gray-700 text-xs tabular-nums">{{ `${judge.avg_rating} ★` }}</span>
251251 </div>
252252 </StxLink>
253253 </div>
@@ -264,8 +264,8 @@
264264 <div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
265265 <div class="flex items-end justify-between">
266266 <div>
267 <h2 class="font-bold text-3xl text-gray-900 tracking-tight" :text="showTopRated() ? 'Top-rated judges' : 'Featured judges'"></h2>
268 <p class="mt-2 text-gray-600" :text="showTopRated() ? 'Highest-rated judges based on published reviews.' : 'A sampling from the directory to get you started.'"></p>
267 <h2 class="font-bold text-3xl text-gray-900 tracking-tight">{{ showTopRated() ? 'Top-rated judges' : 'Featured judges' }}</h2>
268 <p class="mt-2 text-gray-600">{{ showTopRated() ? 'Highest-rated judges based on published reviews.' : 'A sampling from the directory to get you started.' }}</p>
269269 </div>
270270 <StxLink to="/judges" class="hidden sm:block font-semibold text-gray-700 text-sm hover:text-gray-900">
271271 View all judges <span aria-hidden="true">→</span>
@@ -283,12 +283,12 @@
283283 <div class="flex gap-4 items-center">
284284 <img class="object-cover h-16 w-16 ring-2 ring-gray-50 rounded-full grayscale group-hover:grayscale-0 duration-200 transition-all" x-src="judge.image_url" x-alt="judge.name">
285285 <div class="min-w-0">
286 <h3 class="font-semibold text-gray-900 truncate group-hover:text-deep-brown duration-200 transition-colors" :text="judge.name"></h3>
287 <p :if="judge.court_name" class="text-gray-500 text-sm truncate" :text="judge.court_name"></p>
286 <h3 class="font-semibold text-gray-900 truncate group-hover:text-deep-brown duration-200 transition-colors">{{ judge.name }}</h3>
287 <p :if="judge.court_name" class="text-gray-500 text-sm truncate">{{ judge.court_name }}</p>
288288 </div>
289289 </div>
290290 <div class="flex items-center justify-between mt-4 pt-4 border-gray-100 border-t">
291 <span class="font-medium text-amber-700 text-xs tabular-nums" :text="`${judge.avg_rating} ★ · ${judge.review_count} ${judge.review_count === 1 ? 'review' : 'reviews'}`"></span>
291 <span class="font-medium text-amber-700 text-xs tabular-nums">{{ `${judge.avg_rating} ★ · ${judge.review_count} ${judge.review_count === 1 ? 'review' : 'reviews'}` }}</span>
292292 <span class="font-medium text-gray-700 text-xs hover:text-gray-900">View profile →</span>
293293 </div>
294294 </StxLink>
@@ -306,9 +306,9 @@
306306 <div class="flex gap-4 items-center">
307307 <img class="object-cover h-16 w-16 ring-2 ring-gray-50 rounded-full grayscale group-hover:grayscale-0 duration-200 transition-all" x-src="judge.photo" x-alt="judge.name">
308308 <div class="min-w-0">
309 <h3 class="font-semibold text-gray-900 truncate group-hover:text-deep-brown duration-200 transition-colors" :text="judge.name"></h3>
310 <p class="text-gray-500 text-sm truncate" :text="judge.court.name"></p>
311 <p class="text-gray-400 text-xs" :text="judge.location"></p>
309 <h3 class="font-semibold text-gray-900 truncate group-hover:text-deep-brown duration-200 transition-colors">{{ judge.name }}</h3>
310 <p class="text-gray-500 text-sm truncate">{{ judge.court.name }}</p>
311 <p class="text-gray-400 text-xs">{{ judge.location }}</p>
312312 </div>
313313 </div>
314314 <div class="flex items-center justify-between mt-4 pt-4 border-gray-100 border-t">
@@ -398,18 +398,18 @@
398398 >
399399 <div class="flex items-center justify-between">
400400 <div class="flex items-center">
401 <div class="flex items-center justify-center h-9 w-9 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full" :text="(review.title || 'A').slice(0, 1).toUpperCase()"></div>
401 <div class="flex items-center justify-center h-9 w-9 font-semibold text-gray-500 text-sm bg-gray-100 rounded-full">{{ (review.title || 'A').slice(0, 1).toUpperCase() }}</div>
402402 <div class="ml-3">
403403 <p class="font-medium text-gray-900 text-sm">Reviewer</p>
404 <p class="text-gray-500 text-xs" :text="formatDate(review.created_at)"></p>
404 <p class="text-gray-500 text-xs">{{ formatDate(review.created_at) }}</p>
405405 </div>
406406 </div>
407407 <div class="flex items-center">
408408 <Icon :for="star in stars" name="star" size="16" x-class="star <= review.rating ? 'text-yellow-400' : 'text-gray-200'" />
409409 </div>
410410 </div>
411 <h3 class="mt-4 font-semibold line-clamp-2 text-gray-900" :text="review.title"></h3>
412 <p class="mt-2 line-clamp-3 text-gray-600 text-sm" :text="review.content"></p>
411 <h3 class="mt-4 font-semibold line-clamp-2 text-gray-900">{{ review.title }}</h3>
412 <p class="mt-2 line-clamp-3 text-gray-600 text-sm">{{ review.content }}</p>
413413 </StxLink>
414414 </div>
415415 </div>
@@ -433,10 +433,10 @@
433433 <ul class="grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-5">
434434 <li :for="reviewer in activeReviewers" class="p-4 bg-white border border-gray-100 rounded-xl hover:border-emerald-200 hover:shadow-sm transition-all">
435435 <StxLink x-to="`/user/${reviewer.id}`" class="flex items-center gap-3">
436 <div class="flex items-center justify-center flex-none h-10 w-10 font-semibold text-emerald-700 text-xs bg-emerald-50 ring-1 ring-emerald-200/60 rounded-full" :text="(reviewer.name || '?').charAt(0).toUpperCase()"></div>
436 <div class="flex items-center justify-center flex-none h-10 w-10 font-semibold text-emerald-700 text-xs bg-emerald-50 ring-1 ring-emerald-200/60 rounded-full">{{ (reviewer.name || '?').charAt(0).toUpperCase() }}</div>
437437 <div class="min-w-0 flex-1">
438 <p class="font-medium text-gray-900 text-sm truncate" :text="reviewer.name"></p>
439 <p class="text-emerald-700 text-xs" :text="`${reviewer.review_count} ${reviewer.review_count === 1 ? 'review' : 'reviews'}`"></p>
438 <p class="font-medium text-gray-900 text-sm truncate">{{ reviewer.name }}</p>
439 <p class="text-emerald-700 text-xs">{{ `${reviewer.review_count} ${reviewer.review_count === 1 ? 'review' : 'reviews'}` }}</p>
440440 </div>
441441 </StxLink>
442442 </li>
resources/views/judges/submit.stxmodified+1-1
Changes to resources/views/judges/submit.stx
@@ -168,7 +168,7 @@
168168 </div>
169169 </div>
170170
171 <p :show="submitError" class="text-red-600 text-sm" x-text="submitError"></p>
171 <p :show="submitError" class="text-red-600 text-sm">{{ submitError }}</p>
172172
173173 <div class="flex gap-x-4 items-center justify-end">
174174 <StxLink to="/judges" class="font-semibold text-gray-700 text-sm">Cancel</StxLink>
resources/views/login.stxmodified+2-4
Changes to resources/views/login.stx
@@ -108,9 +108,7 @@
108108 role="alert"
109109 aria-live="polite"
110110 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
111 x-class="submitError() ? '' : 'hidden'"
112 :text="submitError()"
113 ></div>
111 x-class="submitError() ? '' : 'hidden'">{{ submitError() }}</div>
114112
115113 <button
116114 type="submit"
@@ -118,7 +116,7 @@
118116 class="inline-flex items-center justify-center px-3 py-1.5 w-full font-semibold text-sm/6 text-white bg-gray-600 hover:bg-gray-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
119117 >
120118 <svg :if="loading()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
121 <span :text="loading() ? 'Signing in...' : 'Sign in'"></span>
119 <span>{{ loading() ? 'Signing in...' : 'Sign in' }}</span>
122120 </button>
123121 </form>
124122 </div>
resources/views/register.stxmodified+2-4
Changes to resources/views/register.stx
@@ -142,9 +142,7 @@
142142 role="alert"
143143 aria-live="polite"
144144 class="px-4 py-3 text-red-800 text-sm bg-red-50 border border-red-200 rounded-md"
145 x-class="submitError() ? '' : 'hidden'"
146 :text="submitError()"
147 ></div>
145 x-class="submitError() ? '' : 'hidden'">{{ submitError() }}</div>
148146
149147 <button
150148 type="submit"
@@ -152,7 +150,7 @@
152150 class="inline-flex items-center justify-center px-3 py-1.5 w-full font-semibold text-sm/6 text-white bg-gray-600 hover:bg-gray-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
153151 >
154152 <svg :if="loading()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg>
155 <span :text="loading() ? 'Creating account...' : 'Sign up'"></span>
153 <span>{{ loading() ? 'Creating account...' : 'Sign up' }}</span>
156154 </button>
157155 </form>
158156 </div>
resources/views/reset-password.stxmodified+2-4
Changes to resources/views/reset-password.stx
@@ -141,15 +141,13 @@
141141 role="alert"
142142 aria-live="polite"
143143 class="text-red-600 text-sm"
144 x-class="submitError() ? '' : 'hidden'"
145 :text="submitError()"
146 ></p>
144 x-class="submitError() ? '' : 'hidden'">{{ submitError() }}</p>
147145
148146 <button
149147 type="submit"
150148 x-disabled="loading"
151149 class="inline-flex items-center justify-center px-3 py-1.5 w-full font-semibold text-sm/6 text-white bg-gray-600 hover:bg-gray-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-gray-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
152 ><svg :if="loading()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span :text="loading() ? 'Saving...' : 'Reset password'"></span></button>
150 ><svg :if="loading()" class="animate-spin -ml-0.5 mr-2 h-4 w-4" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" class="opacity-25"></circle><path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path></svg><span>{{ loading() ? 'Saving...' : 'Reset password' }}</span></button>
153151 </form>
154152 </div>
155153