diff --git a/api/api.go b/api/api.go index abd0988f1..34c212717 100644 --- a/api/api.go +++ b/api/api.go @@ -5,6 +5,8 @@ import ( "io" "net/url" "time" + + "golang.org/x/oauth2" ) //go:generate go tool mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,Tariff @@ -214,7 +216,7 @@ type Tariff interface { // AuthProvider is the ability to provide OAuth authentication through the ui type AuthProvider interface { - Login(state string) (string, error) + Login(state string) (string, *oauth2.DeviceAuthResponse, error) Logout() error HandleCallback(params url.Values) error Authenticated() bool diff --git a/assets/css/app.css b/assets/css/app.css index 510d802aa..9d77cd105 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -564,8 +564,12 @@ input::-webkit-datetime-edit { } input.form-control:disabled, input.form-control:read-only, +textarea.form-control:disabled, +textarea.form-control:read-only, .dark input.form-control:disabled, -.dark input.form-control:read-only { +.dark input.form-control:read-only, +.dark textarea.form-control:disabled, +.dark textarea.form-control:read-only { background-color: var(--evcc-gray-10); color: var(--bs-default-text); opacity: 1; @@ -701,11 +705,29 @@ html.app .modal-dialog { } .dark .round-box--error { + box-shadow: 0 0 0.5rem var(--bs-danger); + border: 1px solid var(--bs-danger); +} + +.dark .round-box--error .tags * { + color: var(--bs-danger) !important; +} + +.round-box--warning { box-shadow: 0 0 0.5rem var(--bs-warning); border: 1px solid var(--bs-warning); } -.dark .round-box--error .tags * { +.round-box--warning .tags * { + color: var(--bs-warning-text-emphasis) !important; +} + +.dark .round-box--warning { + box-shadow: 0 0 0.5rem var(--bs-warning); + border: 1px solid var(--bs-warning); +} + +.dark .round-box--warning .tags * { color: var(--bs-warning) !important; } diff --git a/assets/js/components/Config/AuthCodeDisplay.vue b/assets/js/components/Config/AuthCodeDisplay.vue new file mode 100644 index 000000000..9c757ff56 --- /dev/null +++ b/assets/js/components/Config/AuthCodeDisplay.vue @@ -0,0 +1,53 @@ + + + diff --git a/assets/js/components/Config/AuthConnectButton.vue b/assets/js/components/Config/AuthConnectButton.vue new file mode 100644 index 000000000..98f1beced --- /dev/null +++ b/assets/js/components/Config/AuthConnectButton.vue @@ -0,0 +1,50 @@ + + + diff --git a/assets/js/components/Config/AuthProvidersCard.vue b/assets/js/components/Config/AuthProvidersCard.vue new file mode 100644 index 000000000..2f6fe98fa --- /dev/null +++ b/assets/js/components/Config/AuthProvidersCard.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/assets/js/components/Config/AuthSuccessBanner.vue b/assets/js/components/Config/AuthSuccessBanner.vue index 0048fc5ef..b0363e1de 100644 --- a/assets/js/components/Config/AuthSuccessBanner.vue +++ b/assets/js/components/Config/AuthSuccessBanner.vue @@ -1,6 +1,9 @@ diff --git a/assets/js/components/Config/DeviceCard.vue b/assets/js/components/Config/DeviceCard.vue index f9fe24419..9aa82e1c4 100644 --- a/assets/js/components/Config/DeviceCard.vue +++ b/assets/js/components/Config/DeviceCard.vue @@ -1,5 +1,8 @@ diff --git a/assets/js/components/Top/types.d.ts b/assets/js/components/Top/types.d.ts index 92494593d..68d55b2e5 100644 --- a/assets/js/components/Top/types.d.ts +++ b/assets/js/components/Top/types.d.ts @@ -1,6 +1,5 @@ export interface Provider { title: string; authenticated: boolean; - loginPath: string; - logoutPath: string; + id: string; } diff --git a/assets/js/utils/clipboard.ts b/assets/js/utils/clipboard.ts new file mode 100644 index 000000000..e9ff7c987 --- /dev/null +++ b/assets/js/utils/clipboard.ts @@ -0,0 +1,29 @@ +export function isClipboardSupported(): boolean { + return !!(navigator.clipboard && window.isSecureContext); +} + +export async function copyToClipboard(text: string): Promise { + if (!isClipboardSupported()) { + return false; + } + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + console.error("Failed to copy to clipboard:", err); + return false; + } +} + +export async function copyWithFeedback( + text: string, + setCopiedState: (value: boolean) => void +): Promise { + const success = await copyToClipboard(text); + if (success) { + setCopiedState(true); + setTimeout(() => { + setCopiedState(false); + }, 2000); + } +} diff --git a/assets/js/views/Config.vue b/assets/js/views/Config.vue index 30c85804e..401b8b9c5 100644 --- a/assets/js/views/Config.vue +++ b/assets/js/views/Config.vue @@ -1,7 +1,11 @@