-
-
- {profile.name || t("common.unnamed")}
- {profile.callsign && }
-
-
- {profile.description && (
-
{profile.description}
- )}
- {profile.url && (
-
- {profile.url}
-
- )}
-
- {hasOperatorOrAdmin(profile.roles, config) && (
-
- )}
+ {editMode && isAdmin ? (
+
+
+
{t("user_profile.edit_profile")}
+
+ {editError &&
}
+
{
+ setEditMode(false);
+ setEditError(null);
+ }}
+ onError={(msg) => setEditError(msg)}
+ />
+
+
-
+ ) : (
+
+
+
+ {profile.name || t("common.unnamed")}
+ {profile.callsign && (
+
+ )}
+
+
+ {profile.description && (
+
{profile.description}
+ )}
+ {profile.url && (
+
+ {profile.url}
+
+ )}
+
+ {hasOperatorOrAdmin(profile.roles, config) && (
+
+ )}
+
+
+ )}
>
);
}
@@ -184,7 +341,6 @@ function OwnProfileView() {
const config = useAppConfig();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
- const queryClient = useQueryClient();
const { data: profile, error: queryError } = useQuery({
queryKey: qk.profiles.me(),
queryFn: ({ signal }) =>
@@ -192,17 +348,6 @@ function OwnProfileView() {
});
const error = queryError ? queryError.message : null;
- const updateMutation = useMutation({
- mutationFn: ({
- id,
- body,
- }: {
- id: string;
- body: Record
;
- }) => apiPut(`/api/v1/user/profile/${id}`, body),
- onSuccess: () => invalidate.profiles(queryClient),
- });
-
if (!config.oidc_enabled || !config.user) {
return (
@@ -221,29 +366,6 @@ function OwnProfileView() {
const flashMessage = searchParams.get("message") || "";
const flashError = searchParams.get("error") || "";
- const handleSubmit = async (e: FormEvent
) => {
- e.preventDefault();
- const data = new FormData(e.currentTarget);
- const body = {
- name: String(data.get("name") ?? "").trim() || null,
- callsign: String(data.get("callsign") ?? "").trim() || null,
- description: String(data.get("description") ?? "").trim() || null,
- url: String(data.get("url") ?? "").trim() || null,
- };
- try {
- await updateMutation.mutateAsync({ id: profile.id, body });
- navigate(
- "/profile?message=" + encodeURIComponent(t("user_profile.profile_updated")),
- { replace: true },
- );
- } catch (err) {
- navigate(
- "/profile?error=" + encodeURIComponent((err as Error).message),
- { replace: true },
- );
- }
- };
-
return (
<>
{t("user_profile.your_profile")}
-
+
+ navigate(
+ "/profile?message=" +
+ encodeURIComponent(t("user_profile.profile_updated")),
+ { replace: true },
+ )
+ }
+ onError={(msg) =>
+ navigate(
+ "/profile?error=" + encodeURIComponent(msg),
+ { replace: true },
+ )
+ }
+ />
diff --git a/tests/test_api/test_user_profiles.py b/tests/test_api/test_user_profiles.py
index 65cd10a..641cac6 100644
--- a/tests/test_api/test_user_profiles.py
+++ b/tests/test_api/test_user_profiles.py
@@ -28,6 +28,10 @@ NO_ROLES_HEADERS = {
"X-User-Id": TEST_USER_ID,
"X-User-Roles": "",
}
+ADMIN_HEADERS = {
+ "X-User-Id": OTHER_USER_ID,
+ "X-User-Roles": "admin",
+}
class TestListProfiles:
@@ -383,6 +387,27 @@ class TestUpdateProfile:
)
assert response.status_code == 403
+ def test_update_profile_admin_can_edit_other(
+ self, client_no_auth, sample_user_profile
+ ):
+ """Test that an admin can update another user's profile."""
+ response = client_no_auth.put(
+ f"/api/v1/user/profile/{sample_user_profile.id}",
+ json={
+ "name": "Admin Edited",
+ "callsign": "ADM1",
+ "description": "Admin revised this",
+ "url": "https://example.com/admin",
+ },
+ headers=ADMIN_HEADERS,
+ )
+ assert response.status_code == 200
+ data = response.json()
+ assert data["name"] == "Admin Edited"
+ assert data["callsign"] == "ADM1"
+ assert data["description"] == "Admin revised this"
+ assert data["url"] == "https://example.com/admin"
+
def test_update_profile_rejects_missing_user_id(
self, client_no_auth, sample_user_profile
):