Closes #67 Add opt-in Feedback Hub widget integrated into the Settings Logs card. Routes through a Rust command to bypass CORS and centralize privacy audit. First submission triggers a one-time consent dialog; three opt-in checkboxes (context, logs, identify with Maximus account) all unchecked by default. Wording and payload follow the cross-app conventions in la-compagnie-maximus/docs/feedback-hub-ops.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
1.7 KiB
TypeScript
68 lines
1.7 KiB
TypeScript
import { useCallback, useReducer } from "react";
|
|
import {
|
|
sendFeedback,
|
|
type FeedbackContext,
|
|
type FeedbackErrorCode,
|
|
isFeedbackErrorCode,
|
|
} from "../services/feedbackService";
|
|
|
|
export type FeedbackStatus = "idle" | "sending" | "success" | "error";
|
|
|
|
export interface FeedbackState {
|
|
status: FeedbackStatus;
|
|
errorCode: FeedbackErrorCode | null;
|
|
}
|
|
|
|
export type FeedbackAction =
|
|
| { type: "SEND_START" }
|
|
| { type: "SEND_SUCCESS" }
|
|
| { type: "SEND_ERROR"; code: FeedbackErrorCode }
|
|
| { type: "RESET" };
|
|
|
|
export const initialFeedbackState: FeedbackState = {
|
|
status: "idle",
|
|
errorCode: null,
|
|
};
|
|
|
|
export function feedbackReducer(
|
|
_state: FeedbackState,
|
|
action: FeedbackAction,
|
|
): FeedbackState {
|
|
switch (action.type) {
|
|
case "SEND_START":
|
|
return { status: "sending", errorCode: null };
|
|
case "SEND_SUCCESS":
|
|
return { status: "success", errorCode: null };
|
|
case "SEND_ERROR":
|
|
return { status: "error", errorCode: action.code };
|
|
case "RESET":
|
|
return initialFeedbackState;
|
|
}
|
|
}
|
|
|
|
export interface SubmitArgs {
|
|
content: string;
|
|
userId?: string | null;
|
|
context?: FeedbackContext;
|
|
}
|
|
|
|
export function useFeedback() {
|
|
const [state, dispatch] = useReducer(feedbackReducer, initialFeedbackState);
|
|
|
|
const submit = useCallback(async (args: SubmitArgs) => {
|
|
dispatch({ type: "SEND_START" });
|
|
try {
|
|
await sendFeedback(args);
|
|
dispatch({ type: "SEND_SUCCESS" });
|
|
} catch (e) {
|
|
const code: FeedbackErrorCode = isFeedbackErrorCode(e)
|
|
? e
|
|
: "network_error";
|
|
dispatch({ type: "SEND_ERROR", code });
|
|
}
|
|
}, []);
|
|
|
|
const reset = useCallback(() => dispatch({ type: "RESET" }), []);
|
|
|
|
return { state, submit, reset };
|
|
}
|