diff --git a/app/admin/login/page.tsx b/app/admin/login/page.tsx
index 943b8be..99449ac 100644
--- a/app/admin/login/page.tsx
+++ b/app/admin/login/page.tsx
@@ -10,7 +10,7 @@ export default async function AdminLoginPage() {
if (await getAdminSession()) redirect("/admin");
const turnstileSiteKey = process.env.TURNSTILE_SITE_KEY;
- if (!turnstileSiteKey) {
+ if (process.env.NODE_ENV !== "development" && !turnstileSiteKey) {
throw new Error("TURNSTILE_SITE_KEY is required for administrator login.");
}
@@ -18,7 +18,7 @@ export default async function AdminLoginPage() {
-
+
);
diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx
index baeeda9..7dc04a3 100644
--- a/components/admin/guide-section-editor.tsx
+++ b/components/admin/guide-section-editor.tsx
@@ -248,6 +248,14 @@ type ArtifactProfile = {
goblet: string[];
circlet: string[];
substats: string[];
+ recommendedConditions: Array<{
+ name?: string | null;
+ note: string | null;
+ sands: string[];
+ goblet: string[];
+ circlet: string[];
+ substats: string[];
+ }>;
targets: Array<{ stat: string; value: string }>;
targetConditions: Array<{
name?: string | null;
@@ -745,7 +753,7 @@ function StatSelect({
: undefined}
-
+
{options.map((option) => (
{
+ const source = profile?.recommendedConditions.length
+ ? profile.recommendedConditions
+ : [{ name: null, note: null, sands: profile?.sands ?? [], goblet: profile?.goblet ?? [], circlet: profile?.circlet ?? [], substats: profile?.substats ?? [] }];
+ return source.map((group, index) => ({ ...group, clientId: `recommended-condition-${index}`, name: group.name ?? "", note: group.note ?? "" }));
+ });
+ const [activeRecommendedGroupIndex, setActiveRecommendedGroupIndex] = useState(0);
+ const [renamingRecommendedGroupIndex, setRenamingRecommendedGroupIndex] = useState(null);
+ const recommended = recommendedGroups[activeRecommendedGroupIndex];
+ const { sands, goblet, circlet, substats } = recommended;
+ const setRecommendedStat = (slot: "sands" | "goblet" | "circlet" | "substats", values: string[]) =>
+ setRecommendedGroups((current) => current.map((group, index) => index === activeRecommendedGroupIndex ? { ...group, [slot]: values } : group));
const [targetGroups, setTargetGroups] = useState(() => {
const source = profile?.targetConditions.length
? profile.targetConditions
@@ -1812,10 +1828,11 @@ function ArtifactEditor({
note: row.note?.trim() || null,
})),
})),
- sands,
- goblet,
- circlet,
- substats,
+ sands: recommendedGroups[0].sands,
+ goblet: recommendedGroups[0].goblet,
+ circlet: recommendedGroups[0].circlet,
+ substats: recommendedGroups[0].substats,
+ recommendedGroups: recommendedGroups.map(({ name, note, sands, goblet, circlet, substats }) => ({ name: name.trim() || null, note: note.trim() || null, sands, goblet, circlet, substats })),
targetGroups: targetGroups.map((group) => ({
name: group.name.trim() || null,
note: group.note.trim() || null,
@@ -1836,6 +1853,8 @@ function ArtifactEditor({
goblet,
circlet,
substats,
+ recommendedGroups,
+ activeRecommendedGroupIndex,
targetGroups,
activeTargetGroupIndex,
talents,
@@ -1865,10 +1884,8 @@ function ArtifactEditor({
),
restoreLocalDraft: (draft) => {
setGroups(draft.groups);
- setSands(draft.sands);
- setGoblet(draft.goblet);
- setCirclet(draft.circlet);
- setSubstats(draft.substats);
+ setRecommendedGroups(draft.recommendedGroups?.length ? draft.recommendedGroups : [{ clientId: createClientId("recommended-condition"), name: "", note: "", sands: draft.sands, goblet: draft.goblet, circlet: draft.circlet, substats: draft.substats }]);
+ setActiveRecommendedGroupIndex(Math.min(draft.activeRecommendedGroupIndex ?? 0, Math.max(0, (draft.recommendedGroups?.length ?? 1) - 1)));
setTargetGroups(draft.targetGroups.map((group) => ({ ...group, name: group.name ?? "" })));
setActiveTargetGroupIndex(draft.activeTargetGroupIndex ?? 0);
setTalents(draft.talents);
@@ -2177,36 +2194,79 @@ function ArtifactEditor({
))}
-
-
- Recommended Stats
+
+
+ Recommended Stats
+
+
{
+ const reordered = reorderConditions(recommendedGroups, activeRecommendedGroupIndex, from, to);
+ setRecommendedGroups(reordered.items);
+ setActiveRecommendedGroupIndex(reordered.activeIndex);
+ }}
+ />
+ { if (!open) setRenamingRecommendedGroupIndex(null); }}
+ onChange={(name) => setRecommendedGroups(recommendedGroups.map((group, index) => index === renamingRecommendedGroupIndex ? { ...group, name } : group))}
+ />
+
+
+
+
+
+
+
+
+ Condition note (optional)
+
+
setRecommendedStat("sands", values)}
elementIconUrls={elementIconUrls}
+ priorityColors
/>
setRecommendedStat("goblet", values)}
elementIconUrls={elementIconUrls}
+ priorityColors
/>
setRecommendedStat("circlet", values)}
elementIconUrls={elementIconUrls}
+ priorityColors
/>
setRecommendedStat("substats", values)}
elementIconUrls={elementIconUrls}
priorityColors
/>
+ {recommendedGroups.length > 1 ? (
+
+ ) : null}
diff --git a/components/admin/login-card.tsx b/components/admin/login-card.tsx
index b26f7d0..cdcee89 100644
--- a/components/admin/login-card.tsx
+++ b/components/admin/login-card.tsx
@@ -50,6 +50,7 @@ interface LoginCardProps {
export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
const router = useRouter();
+ const turnstileEnabled = process.env.NODE_ENV !== "development";
const turnstileContainerRef = useRef(null);
const turnstileWidgetIdRef = useRef(null);
const [loading, setLoading] = useState(false);
@@ -58,6 +59,7 @@ export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
const renderTurnstile = useCallback(() => {
if (
+ !turnstileEnabled ||
!window.turnstile ||
!turnstileContainerRef.current ||
turnstileWidgetIdRef.current
@@ -80,7 +82,7 @@ export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
},
},
);
- }, [turnstileSiteKey]);
+ }, [turnstileEnabled, turnstileSiteKey]);
function resetTurnstile() {
setTurnstileToken(null);
@@ -91,7 +93,7 @@ export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
async function signIn(event: FormEvent) {
event.preventDefault();
- if (!turnstileToken) return;
+ if (turnstileEnabled && !turnstileToken) return;
setLoading(true);
setError(null);
@@ -105,7 +107,9 @@ export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
password,
rememberMe: true,
fetchOptions: {
- headers: { "x-captcha-response": turnstileToken },
+ headers: turnstileEnabled && turnstileToken
+ ? { "x-captcha-response": turnstileToken }
+ : undefined,
},
});
if (result.error) {
@@ -161,19 +165,21 @@ export function LoginCard({ turnstileSiteKey }: LoginCardProps) {
required
/>
-
-
-