feat: updated identity providers in admin panel
All checks were successful
Build and Push App Image / build-and-push (push) Successful in 49s
All checks were successful
Build and Push App Image / build-and-push (push) Successful in 49s
This commit is contained in:
187
frontend/src/components/AdminProviderModal.vue
Normal file
187
frontend/src/components/AdminProviderModal.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<teleport to="body">
|
||||
<div class="modal fade show d-block admin-modal" tabindex="-1" role="dialog" aria-modal="true" @click.self="emit('close')">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ mode === "create" ? "Add Identity Provider" : "Edit Identity Provider" }}</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @click="emit('close')"></button>
|
||||
</div>
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="modal-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Display Name <span class="text-danger">*</span></label>
|
||||
<input v-model="form.name" type="text" class="form-control" required />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Provider Type <span class="text-danger">*</span></label>
|
||||
<select v-model="form.type" class="form-select">
|
||||
<option value="oidc">OIDC</option>
|
||||
<option value="oauth2">OAuth2</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Client ID <span class="text-danger">*</span></label>
|
||||
<input v-model="form.client_id" type="text" class="form-control" required />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">
|
||||
Client Secret
|
||||
<span v-if="mode === 'create'" class="text-danger">*</span>
|
||||
<span v-else class="text-muted small">(leave blank to keep existing)</span>
|
||||
</label>
|
||||
<input v-model="form.client_secret" type="password" class="form-control" :required="mode === 'create'" autocomplete="new-password" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Authorization URL <span class="text-danger">*</span></label>
|
||||
<input v-model="form.authorization_url" type="url" class="form-control" required />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Token URL <span class="text-danger">*</span></label>
|
||||
<input v-model="form.token_url" type="url" class="form-control" required />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">UserInfo URL</label>
|
||||
<input v-model="form.userinfo_url" type="url" class="form-control" placeholder="Optional" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">ID Token Claim</label>
|
||||
<input v-model="form.id_token_claim" type="text" class="form-control" placeholder="id_token" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Scopes</label>
|
||||
<input v-model="form.scopes" type="text" class="form-control" placeholder="openid, profile, email" />
|
||||
<div class="form-text">Comma-separated list of OAuth scopes.</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input id="provider-active" v-model="form.is_active" type="checkbox" class="form-check-input" />
|
||||
<label for="provider-active" class="form-check-label">Provider is active</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" @click="emit('close')">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="submitting">
|
||||
{{ submitting ? "Saving..." : mode === "create" ? "Add Provider" : "Save Changes" }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop fade show admin-modal-backdrop"></div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: "create",
|
||||
},
|
||||
provider: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
submitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["close", "submit"]);
|
||||
|
||||
const form = ref({
|
||||
name: "",
|
||||
type: "oidc",
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
authorization_url: "",
|
||||
token_url: "",
|
||||
userinfo_url: "",
|
||||
id_token_claim: "id_token",
|
||||
scopes: "openid, profile, email",
|
||||
is_active: true,
|
||||
});
|
||||
|
||||
const hydrateForm = () => {
|
||||
if (props.mode === "edit" && props.provider) {
|
||||
form.value = {
|
||||
name: props.provider.name || "",
|
||||
type: props.provider.type || "oidc",
|
||||
client_id: props.provider.client_id || "",
|
||||
client_secret: "",
|
||||
authorization_url: props.provider.authorization_url || "",
|
||||
token_url: props.provider.token_url || "",
|
||||
userinfo_url: props.provider.userinfo_url || "",
|
||||
id_token_claim: props.provider.id_token_claim || "id_token",
|
||||
scopes: (props.provider.scopes || []).join(", "),
|
||||
is_active: props.provider.is_active ?? true,
|
||||
};
|
||||
} else {
|
||||
form.value = {
|
||||
name: "",
|
||||
type: "oidc",
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
authorization_url: "",
|
||||
token_url: "",
|
||||
userinfo_url: "",
|
||||
id_token_claim: "id_token",
|
||||
scopes: "openid, profile, email",
|
||||
is_active: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
watch(() => [props.mode, props.provider], hydrateForm, { immediate: true });
|
||||
|
||||
const handleSubmit = () => {
|
||||
emit("submit", {
|
||||
name: form.value.name,
|
||||
type: form.value.type,
|
||||
client_id: form.value.client_id,
|
||||
client_secret: form.value.client_secret,
|
||||
authorization_url: form.value.authorization_url,
|
||||
token_url: form.value.token_url,
|
||||
userinfo_url: form.value.userinfo_url,
|
||||
id_token_claim: form.value.id_token_claim,
|
||||
scopes: form.value.scopes
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean),
|
||||
is_active: form.value.is_active,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-modal {
|
||||
z-index: 2000;
|
||||
overflow-y: auto;
|
||||
padding-top: max(0.5rem, env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
.admin-modal-backdrop {
|
||||
z-index: 1990;
|
||||
}
|
||||
|
||||
.admin-modal .modal-dialog {
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.admin-modal {
|
||||
padding-top: max(0.75rem, env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
.admin-modal .modal-dialog {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user