From de223b2279af33717d3e09004ea465ed9f793abc Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Mon, 24 Nov 2025 14:20:37 +0100 Subject: [PATCH 1/7] feat: implement new React components and update CI/CD workflow - Added new components: Header, Hero, GettingStarted, ResourceCards - Updated App component to include new components - Enhanced styling with new CSS for layout and design - Modified CI/CD workflow to support additional branches and schema simplification script --- ...e-static-web-apps-gray-stone-017611203.yml | 17 +- docs/css/main.css | 12 + docs/index.html | 5 + index.html | 189 +--------- openapi/SimplifySchemaNames.cjs | 112 ++++++ src/App.jsx | 19 ++ src/components/GettingStarted.jsx | 39 +++ src/components/Header.jsx | 20 ++ src/components/Hero.jsx | 26 ++ src/components/ResourceCards.jsx | 60 ++++ src/index.css | 322 ++++++++++++++++++ src/main.jsx | 10 + 12 files changed, 645 insertions(+), 186 deletions(-) create mode 100644 openapi/SimplifySchemaNames.cjs create mode 100644 src/App.jsx create mode 100644 src/components/GettingStarted.jsx create mode 100644 src/components/Header.jsx create mode 100644 src/components/Hero.jsx create mode 100644 src/components/ResourceCards.jsx create mode 100644 src/index.css create mode 100644 src/main.jsx diff --git a/.github/workflows/azure-static-web-apps-gray-stone-017611203.yml b/.github/workflows/azure-static-web-apps-gray-stone-017611203.yml index 1d29a5b..3354732 100644 --- a/.github/workflows/azure-static-web-apps-gray-stone-017611203.yml +++ b/.github/workflows/azure-static-web-apps-gray-stone-017611203.yml @@ -4,6 +4,8 @@ on: push: branches: - main + - dev + - staging pull_request: types: [opened, synchronize, reopened, closed] branches: @@ -20,20 +22,16 @@ jobs: submodules: true lfs: false - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - name: Remove system namespaces - run: python ./openapi/remove_namespaces_from_doc.py - - name: Show removed namespaces - run: git diff --name-only || true - - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' + + - name: Simplify schema names + run: node ./openapi/SimplifySchemaNames.cjs ./openapi/swagger.json + - name: Show simplified schemas + run: git diff --name-only || true - name: Install dependencies run: npm install @@ -48,6 +46,7 @@ jobs: id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: + production_branch: "main" azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_GRAY_STONE_017611203 }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) action: "upload" diff --git a/docs/css/main.css b/docs/css/main.css index 477f80b..e208385 100644 --- a/docs/css/main.css +++ b/docs/css/main.css @@ -41,6 +41,18 @@ body { padding-right: 20px; } +.docs-logo { + padding: 15px; + background: white; + border-bottom: 1px solid #e1e1e1; + text-align: center; +} + +.docs-logo img { + height: 35px; + object-fit: contain; +} + .index ul.root { padding: 0px; } diff --git a/docs/index.html b/docs/index.html index 02c3483..9264beb 100644 --- a/docs/index.html +++ b/docs/index.html @@ -12,6 +12,11 @@
+
    diff --git a/index.html b/index.html index e0cace2..9e03737 100644 --- a/index.html +++ b/index.html @@ -1,178 +1,13 @@ - - - - - PowerOffice Go Api - - - - - - - -
    - -
    - -
    - -
    -
    -
    - orange sky background -
    -
    -

    Welcome to the PowerOffice Go API

    - Hello Developers! We in the PowerOffice Go team aim to provide you with - a simple to use, yet powerful API to interface your application with PowerOffice Go. -

    - This site is the starting point and the hub for finding information about developing - third party application that interacts with PowerOffice Go -

    -
    -
    -
    - - - - - -
    - - - -
    - + + + + + + PowerOffice Go API + + + +
    + + diff --git a/openapi/SimplifySchemaNames.cjs b/openapi/SimplifySchemaNames.cjs new file mode 100644 index 0000000..3381c11 --- /dev/null +++ b/openapi/SimplifySchemaNames.cjs @@ -0,0 +1,112 @@ +// SimplifySchemaNames.js +// Usage: node SimplifySchemaNames.js ./openapi/swagger.json +// Simplifies verbose .NET type names in OpenAPI schemas +// Example: "One.Common.Api.DataPage`1[[GoApi.Voucher.OutgoingInvoiceVoucher, GoApi, Version=2.59.0.0, Culture=neutral, PublicKeyToken=null]]" +// Becomes: "GoApi.Voucher.OutgoingInvoiceVoucher, GoApi, Version=2.59.0.0" + +const fs = require('fs'); + +const filePath = process.argv[2]; +if (!filePath) { + console.error('Usage: node SimplifySchemaNames.js '); + process.exit(1); +} + +// Read the file +let content = fs.readFileSync(filePath, 'utf8'); +const json = JSON.parse(content); + +/** + * Simplifies a verbose .NET type name by extracting the actual type and removing unnecessary suffixes. + * + * Handles generic type names of the form: + * SomeWrapper`1[[ActualType, Assembly, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null]] + * and non-generic type names with ", Culture=..., PublicKeyToken=...". + * + * @param {string} typeName - The .NET type name to simplify. + * @returns {string} The simplified type name with only the essential type and assembly information. + */ +function simplifyTypeName(typeName) { + // Pattern: Extract content between [[ and ]], then remove Culture and PublicKeyToken + // Matches: SomeWrapper`1[[ActualType, Assembly, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null]] + const genericMatch = typeName.match(/^[^[]+\[\[([^\]]+)\]\]$/); + + if (genericMatch) { + // Extract the inner type + let innerType = genericMatch[1]; + // Remove ", Culture=neutral, PublicKeyToken=null" + innerType = innerType.replace(/, Culture=[^,]+, PublicKeyToken=[^\]]+$/, ''); + return innerType; + } +/** + * Recursively processes an object to update schema names in $ref values throughout the OpenAPI document. + * + * @param {object} obj - The object to process. This can be any part of the OpenAPI document. + * @param {Array} [path=[]] - The current traversal path in the object tree, used for recursion and debugging. + */ + // Also handle non-generic types that might have Culture/PublicKeyToken + if (typeName.includes('Culture=') || typeName.includes('PublicKeyToken=')) { + return typeName.replace(/, Culture=[^,]+, PublicKeyToken=[^\]]+$/, ''); + } + + return typeName; +} + +// Function to recursively process an object and update schema names +function processObject(obj, path = []) { + if (typeof obj !== 'object' || obj === null) { + return; + } + + for (const [key, value] of Object.entries(obj)) { + if (key === '$ref' && typeof value === 'string') { + // Process $ref values + if (value.startsWith('#/components/schemas/')) { + const schemaName = value.substring('#/components/schemas/'.length); + const simplifiedName = simplifyTypeName(schemaName); + if (simplifiedName !== schemaName) { + obj[key] = '#/components/schemas/' + simplifiedName; + } + } + } else if (typeof value === 'object' && value !== null) { + processObject(value, [...path, key]); + } + } +} + +// Rename schema keys in components/schemas +if (json.components && json.components.schemas) { + const schemas = json.components.schemas; + const renamedSchemas = {}; + const renameMap = {}; + + for (const [oldName, schema] of Object.entries(schemas)) { + const newName = simplifyTypeName(oldName); + renameMap[oldName] = newName; + renamedSchemas[newName] = schema; + } + + json.components.schemas = renamedSchemas; + + console.log(`Renamed ${Object.keys(renameMap).length} schemas`); + + // Show some examples of renamed schemas + const examples = Object.entries(renameMap) + .filter(([old, newName]) => old !== newName) + .slice(0, 5); + + if (examples.length > 0) { + console.log('\nExample renames:'); + examples.forEach(([old, newName]) => { + console.log(` - ${old.substring(0, 80)}${old.length > 80 ? '...' : ''}`); + console.log(` → ${newName}`); + }); + } +} + +// Update all $ref references throughout the document +processObject(json); + +// Write back to file +fs.writeFileSync(filePath, JSON.stringify(json, null, 2), 'utf8'); +console.log('\nSchema names simplified successfully.'); diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..fded0aa --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Header from './components/Header'; +import Hero from './components/Hero'; +import GettingStarted from './components/GettingStarted'; +import ResourceCards from './components/ResourceCards'; + +function App() { + return ( +
    +
    + + + +
    +
    + ); +} + +export default App; diff --git a/src/components/GettingStarted.jsx b/src/components/GettingStarted.jsx new file mode 100644 index 0000000..c1e7dbd --- /dev/null +++ b/src/components/GettingStarted.jsx @@ -0,0 +1,39 @@ +import React from 'react'; + +const GettingStarted = () => { + const steps = [ + { + number: 1, + title: 'Register Your Application', + link: '/docs/index.html#Common/Registration.md' + }, + { + number: 2, + title: 'Read Documentation', + link: '/docs/index.html#Welcome.md' + }, + { + number: 3, + title: 'Start Coding', + link: '/docs/index.html#Sdk/Tutorials/GettingStarted.md' + } + ]; + + return ( +
    +
    + {steps.map((step, index) => ( + + + {step.number} + {step.title} + + {index < steps.length - 1 &&
    } + + ))} +
    +
    + ); +}; + +export default GettingStarted; diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 0000000..5a8362b --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const Header = () => { + return ( +
    +
    +
    + PowerOffice Go logo +
    + +
    +
    + ); +}; + +export default Header; diff --git a/src/components/Hero.jsx b/src/components/Hero.jsx new file mode 100644 index 0000000..63cca30 --- /dev/null +++ b/src/components/Hero.jsx @@ -0,0 +1,26 @@ +import React from 'react'; + +const Hero = () => { + return ( +
    +
    +
    + PowerOffice Go logo against an orange sky background +
    +
    +

    Welcome to the PowerOffice Go API

    +

    + Hello Developers! We in the PowerOffice Go team aim to provide you with + a simple to use, yet powerful API to interface your application with PowerOffice Go. +

    +

    + This site is the starting point and the hub for finding information about developing + third party applications that interact with PowerOffice Go. +

    +
    +
    +
    + ); +}; + +export default Hero; diff --git a/src/components/ResourceCards.jsx b/src/components/ResourceCards.jsx new file mode 100644 index 0000000..df56915 --- /dev/null +++ b/src/components/ResourceCards.jsx @@ -0,0 +1,60 @@ +import React from 'react'; + +const ResourceCards = () => { + const resources = [ + { + title: 'Resources', + links: [ + { text: 'C# Documentation', href: '/docs/index.html#Sdk/Introduction.md' }, + { text: 'REST Documentation', href: '/docs/index.html#Rest/Introduction.md' }, + { text: 'OpenAPI Documentation', href: '/openapi/index.html' }, + { text: 'PowerOffice Go at GitHub', href: 'https://github.com/PowerOffice/go-api/', external: true }, + { text: 'PowerOffice Go SDK at NuGet', href: 'https://www.nuget.org/packages/PowerOfficeGoSdk/', external: true }, + { text: 'PowerOffice Go Home', href: 'http://www.poweroffice.no/go', external: true } + ] + }, + { + title: 'Examples', + links: [ + { text: 'C# Getting Started', href: '/docs/index.html#Sdk/Tutorials/GettingStarted.md' }, + { text: 'C# Examples', href: 'https://github.com/PowerOffice/go-api/tree/master/Examples', external: true }, + { text: 'REST Getting Started', href: '/docs/index.html#Rest/Introduction.md' }, + { text: 'REST Examples', href: '/docs/index.html#Rest/Examples.md' } + ] + }, + { + title: 'Support', + links: [ + { text: 'Issue Tracker', href: 'https://github.com/PowerOffice/go-api/issues', external: true }, + { text: 'Contact Us', href: 'mailto:go-api@poweroffice.no', external: true } + ] + } + ]; + + return ( +
    +
    + {resources.map((section) => ( +
    +

    {section.title}

    + +
    + ))} +
    +
    + ); +}; + +export default ResourceCards; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..531484f --- /dev/null +++ b/src/index.css @@ -0,0 +1,322 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --primary-color: #ea802c; + --primary-hover: #fa903c; + --primary-dark: #ca600c; + --text-color: #333; + --text-light: #666; + --border-color: #e0e0e0; + --bg-light: #f8f9fa; + --white: #ffffff; + --shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + --shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.15); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 16px; + color: var(--text-color); + line-height: 1.6; + background-color: var(--white); +} + +.app { + min-height: 100vh; + position: relative; + overflow-x: hidden; +} + +/* Header */ +.header { + background: var(--white); + box-shadow: var(--shadow); + position: sticky; + top: 0; + z-index: 1000; + width: 100%; +} + +.header-container { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo img { + height: 40px; + display: block; + object-fit: contain; +} + +.nav { + display: flex; + gap: 2rem; + align-items: center; +} + +.nav a { + color: var(--text-light); + text-decoration: none; + font-weight: 500; + transition: color 0.3s ease; +} + +.nav a:hover { + color: var(--primary-color); +} + +/* Hero Section */ +.hero { + background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%); + padding: 4rem 2rem; + position: relative; +} + +.hero-container { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; +} + +.hero-image img { + width: 100%; + max-width: 500px; + height: auto; +} + +.hero-content h1 { + font-size: 2.5rem; + color: var(--text-color); + margin-bottom: 1rem; + line-height: 1.2; +} + +.hero-subtitle { + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--text-light); +} + +.hero-subtitle strong { + color: var(--text-color); +} + +.hero-content p { + color: var(--text-light); + font-size: 1rem; + line-height: 1.7; +} + +/* Getting Started Section */ +.getting-started { + padding: 5rem 2rem; + background: var(--white); +} + +.getting-started-container { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + gap: 2rem; + flex-wrap: wrap; +} + +.step-card { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 2rem 2.5rem; + background: var(--primary-color); + color: var(--white); + text-decoration: none; + border-radius: 12px; + min-width: 280px; + transition: all 0.3s ease; + box-shadow: var(--shadow); +} + +.step-card:hover { + background: var(--primary-hover); + transform: translateY(-4px); + box-shadow: var(--shadow-hover); +} + +.step-number { + display: inline-block; + width: 50px; + height: 50px; + line-height: 50px; + background: var(--primary-dark); + border-radius: 50%; + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 1rem; +} + +.step-title { + font-size: 1.25rem; + font-weight: 600; +} + +.step-connector { + width: 60px; + height: 2px; + background: var(--border-color); + position: relative; +} + +.step-connector::after { + content: '→'; + position: absolute; + right: -10px; + top: -12px; + color: var(--border-color); + font-size: 1.5rem; +} + +/* Resources Section */ +.resources { + padding: 4rem 2rem; + background: var(--bg-light); +} + +.resources-container { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.resource-card { + background: var(--white); + padding: 2rem; + border-radius: 12px; + border-left: 5px solid var(--primary-color); + box-shadow: var(--shadow); + transition: all 0.3s ease; +} + +.resource-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-hover); +} + +.resource-card h3 { + color: var(--text-color); + font-size: 1.5rem; + margin-bottom: 1.5rem; + font-weight: 600; +} + +.resource-card ul { + list-style: none; +} + +.resource-card li { + margin-bottom: 0.75rem; +} + +.resource-card a { + color: var(--text-light); + text-decoration: none; + transition: color 0.3s ease; + display: inline-block; +} + +.resource-card a:hover { + color: var(--primary-color); + text-decoration: underline; +} + +/* Skybox Background */ +.skybox { + position: fixed; + top: 80px; + left: 0; + right: 0; + height: 400px; + background: url(/Bakgrunn_skyer.png) center/cover no-repeat; + z-index: -1; + opacity: 0.3; + pointer-events: none; +} + +/* Responsive Design */ +@media (max-width: 968px) { + .hero-container { + grid-template-columns: 1fr; + text-align: center; + } + + .hero-image { + order: -1; + } + + .hero-content h1 { + font-size: 2rem; + } + + .nav { + gap: 1rem; + } + + .step-connector { + display: none; + } + + .getting-started-container { + flex-direction: column; + } +} + +@media (max-width: 640px) { + .header-container { + flex-direction: column; + gap: 1rem; + } + + .nav { + flex-direction: column; + gap: 0.5rem; + } + + .hero { + padding: 2rem 1rem; + } + + .hero-content h1 { + font-size: 1.75rem; + } + + .getting-started { + padding: 3rem 1rem; + } + + .step-card { + min-width: 100%; + } + + .resources { + padding: 2rem 1rem; + } + + .resources-container { + grid-template-columns: 1fr; + } +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..7497ae8 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.jsx'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +); From ae145228b6b0b677e2dc80cf6319d92a6202b372 Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Mon, 24 Nov 2025 14:30:34 +0100 Subject: [PATCH 2/7] removed reference to swag.json --- vite.config.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vite.config.js b/vite.config.js index 8a3119d..30e7f2b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -9,18 +9,14 @@ const copyStaticFilesPlugin = () => ({ closeBundle() { const distDir = join(process.cwd(), 'dist') - // Copy swagger.json and swag.json to dist/openapi + // Copy swagger.json to dist/openapi const openapiDestDir = join(distDir, 'openapi') mkdirSync(openapiDestDir, { recursive: true }) copyFileSync( join(process.cwd(), 'openapi', 'swagger.json'), join(openapiDestDir, 'swagger.json') ) - copyFileSync( - join(process.cwd(), 'openapi', 'swag.json'), - join(openapiDestDir, 'swag.json') - ) - console.log('✓ Copied swagger.json and swag.json to dist/openapi') + console.log('✓ Copied swagger.jso to dist/openapi') // Copy docs folder const docsSource = join(process.cwd(), 'docs') From 7cc1b6a14df4bbca405a886cb5de7da1868e855d Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Mon, 24 Nov 2025 14:42:19 +0100 Subject: [PATCH 3/7] added darkmode --- docs/api/common/registration.md | 2 +- docs/css/main.css | 112 ++++++++++++++++++++++++++++++++ docs/index.html | 21 ++++++ docs/scripts/main.js | 29 ++++++++- src/App.jsx | 22 ++++++- src/components/Header.jsx | 26 +++++++- src/index.css | 92 ++++++++++++++++++++++++++ 7 files changed, 299 insertions(+), 5 deletions(-) diff --git a/docs/api/common/registration.md b/docs/api/common/registration.md index 499f1b4..4169f9d 100644 --- a/docs/api/common/registration.md +++ b/docs/api/common/registration.md @@ -14,7 +14,7 @@ Each application that will connect to PowerOffice Go using the Go-Api must be re Once your PowerOffice Go user has been activated, you can register an application and receive an *application key*. ->Currenly you must email us to receive an application key. To apply for an application to use PowerOffice Go API, send an e-mail to [go-api@poweroffice.no](mailto:go-api@poweroffice.no) stating your company name, system name, who you're representing, contact e-mail address and the purpose of the integration. +>Currently you must email us to receive an application key. To apply for an application to use PowerOffice Go API, send an e-mail to [go-api@poweroffice.no](mailto:go-api@poweroffice.no) stating your company name, system name, who you're representing, contact e-mail address and the purpose of the integration. ### Application Key diff --git a/docs/css/main.css b/docs/css/main.css index e208385..837a93a 100644 --- a/docs/css/main.css +++ b/docs/css/main.css @@ -2,10 +2,67 @@ height: 100%; margin: 0; padding: 0; + transition: background-color 0.3s ease, color 0.3s ease; } body { font-family: "MuseoSans","Segoe UI",Arial,sans-serif; + background-color: #ffffff; + color: #333333; +} + +body.dark-mode { + background-color: #09090b; + color: #e4e4e7; +} + +body.dark-mode .content { + color: #e4e4e7; +} + +body.dark-mode pre { + background-color: #27272a !important; + color: #e4e4e7 !important; +} + +body.dark-mode code { + background-color: #27272a !important; + color: #e4e4e7 !important; +} + +body.dark-mode table { + border-color: #3f3f46 !important; + background-color: #18181b !important; +} + +body.dark-mode th { + border-color: #3f3f46 !important; + background-color: #27272a !important; + color: #fafafa !important; + font-weight: 600; +} + +body.dark-mode td { + border-color: #3f3f46 !important; + background-color: #18181b !important; + color: #e4e4e7 !important; +} + +body.dark-mode tbody tr:hover td { + background-color: #27272a !important; +} + +body.dark-mode a { + color: #fa903c; +} + +body.dark-mode h1, +body.dark-mode h2, +body.dark-mode h3, +body.dark-mode h4, +body.dark-mode h5, +body.dark-mode h6 { + color: #e4e4e7; } #leftmenu { @@ -15,6 +72,11 @@ body { overflow: scroll; white-space: nowrap; background-color: #f1f1f1; + transition: background-color 0.3s ease; +} + +body.dark-mode #leftmenu { + background-color: #18181b; } #leftmenu::-webkit-scrollbar { @@ -46,6 +108,13 @@ body { background: white; border-bottom: 1px solid #e1e1e1; text-align: center; + position: relative; + transition: background-color 0.3s ease, border-color 0.3s ease; +} + +body.dark-mode .docs-logo { + background: #27272a; + border-bottom: 1px solid #3f3f46; } .docs-logo img { @@ -53,6 +122,40 @@ body { object-fit: contain; } +.dark-mode-toggle { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + cursor: pointer; + padding: 8px; + display: flex; + align-items: center; + justify-content: center; + color: #666; + transition: all 0.3s ease; + border-radius: 8px; +} + +.dark-mode-toggle:hover { + color: #ea802c; + background: #f8f9fa; +} + +body.dark-mode .dark-mode-toggle { + color: #a1a1aa; +} + +body.dark-mode .dark-mode-toggle:hover { + color: #fa903c; + background: #18181b; +} + +.dark-mode-toggle svg { + display: block; +} + .index ul.root { padding: 0px; } @@ -72,12 +175,21 @@ body { padding: 5px; white-space: nowrap; color: black; + transition: background-color 0.3s ease, color 0.3s ease; +} + +body.dark-mode .index li a { + color: #e4e4e7; } .index li a:hover { background-color: #e1e1e1; } +body.dark-mode .index li a:hover { + background-color: #27272a; +} + .treemenu li { list-style: none; } diff --git a/docs/index.html b/docs/index.html index 9264beb..ebcdee6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -16,6 +16,27 @@ PowerOffice Go +
      diff --git a/docs/scripts/main.js b/docs/scripts/main.js index 4c03f49..ab2cd1f 100644 --- a/docs/scripts/main.js +++ b/docs/scripts/main.js @@ -1,4 +1,31 @@ -godocs = { +// Dark mode functionality +function initDarkMode() { + const darkMode = localStorage.getItem('darkMode') === 'true'; + if (darkMode) { + document.body.classList.add('dark-mode'); + document.getElementById('sunIcon').style.display = 'block'; + document.getElementById('moonIcon').style.display = 'none'; + } + + const toggle = document.getElementById('darkModeToggle'); + if (toggle) { + toggle.addEventListener('click', function() { + const isDark = document.body.classList.toggle('dark-mode'); + localStorage.setItem('darkMode', isDark); + document.getElementById('sunIcon').style.display = isDark ? 'block' : 'none'; + document.getElementById('moonIcon').style.display = isDark ? 'none' : 'block'; + }); + } +} + +// Initialize on page load +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initDarkMode); +} else { + initDarkMode(); +} + +godocs = { mainUrl: 'Welcome.md', reloadContent() { diff --git a/src/App.jsx b/src/App.jsx index fded0aa..e471ddb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,13 +1,31 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Header from './components/Header'; import Hero from './components/Hero'; import GettingStarted from './components/GettingStarted'; import ResourceCards from './components/ResourceCards'; function App() { + const [darkMode, setDarkMode] = useState(() => { + const saved = localStorage.getItem('darkMode'); + return saved ? JSON.parse(saved) : false; + }); + + useEffect(() => { + localStorage.setItem('darkMode', JSON.stringify(darkMode)); + if (darkMode) { + document.documentElement.classList.add('dark-mode'); + } else { + document.documentElement.classList.remove('dark-mode'); + } + }, [darkMode]); + + const toggleDarkMode = () => { + setDarkMode(!darkMode); + }; + return (
      -
      +
      diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 5a8362b..26674df 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,6 +1,6 @@ import React from 'react'; -const Header = () => { +const Header = ({ darkMode, toggleDarkMode }) => { return (
      @@ -11,6 +11,30 @@ const Header = () => { Documentation Issue Tracker Contact +
      diff --git a/src/index.css b/src/index.css index 531484f..c3b99df 100644 --- a/src/index.css +++ b/src/index.css @@ -17,6 +17,23 @@ --shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.15); } +.dark-mode { + --primary-color: #fa903c; + --primary-hover: #ffa04c; + --primary-dark: #ea802c; + --text-color: #e4e4e7; + --text-light: #a1a1aa; + --border-color: #3f3f46; + --bg-light: #18181b; + --white: #27272a; + --shadow: 0 2px 8px rgba(0, 0, 0, 0.4); + --shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.6); +} + +.dark-mode body { + background-color: #09090b; +} + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 16px; @@ -73,6 +90,28 @@ body { color: var(--primary-color); } +.dark-mode-toggle { + background: none; + border: none; + cursor: pointer; + padding: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-light); + transition: all 0.3s ease; + border-radius: 8px; +} + +.dark-mode-toggle:hover { + color: var(--primary-color); + background: var(--bg-light); +} + +.dark-mode-toggle svg { + display: block; +} + /* Hero Section */ .hero { background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%); @@ -80,6 +119,10 @@ body { position: relative; } +.dark-mode .hero { + background: linear-gradient(135deg, #18181b 0%, #27272a 100%); +} + .hero-container { max-width: 1200px; margin: 0 auto; @@ -189,6 +232,50 @@ body { font-size: 1.5rem; } +/* Table Styles */ +table { + border-collapse: collapse; + width: 100%; +} + +th, td { + border: 1px solid var(--border-color); + padding: 0.75rem; + text-align: left; +} + +th { + background-color: var(--bg-light); + font-weight: 600; + color: var(--text-color); +} + +td { + color: var(--text-color); +} + +tbody tr:hover { + background-color: var(--bg-light); +} + +.dark-mode table { + border-color: var(--border-color); +} + +.dark-mode th { + background-color: #27272a; + color: #fafafa; +} + +.dark-mode td { + background-color: #18181b; + color: #e4e4e7; +} + +.dark-mode tbody tr:hover { + background-color: #27272a; +} + /* Resources Section */ .resources { padding: 4rem 2rem; @@ -257,6 +344,11 @@ body { pointer-events: none; } +.dark-mode .skybox { + opacity: 0.1; + filter: brightness(0.6); +} + /* Responsive Design */ @media (max-width: 968px) { .hero-container { From f4a05e5ccb9528b720e30f06210726cda8a8010f Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Mon, 24 Nov 2025 15:55:00 +0100 Subject: [PATCH 4/7] feat: add Backend Changes Notice component and update styles for backend changes --- backend-changes.html | 367 ++++++++++++++++++++++++ src/App.jsx | 2 + src/components/BackendChangesNotice.jsx | 27 ++ src/index.css | 93 ++++++ 4 files changed, 489 insertions(+) create mode 100644 backend-changes.html create mode 100644 src/components/BackendChangesNotice.jsx diff --git a/backend-changes.html b/backend-changes.html new file mode 100644 index 0000000..84e46cb --- /dev/null +++ b/backend-changes.html @@ -0,0 +1,367 @@ + + + + + + Backend Changes - PowerOffice Go API + + + + +
      +
      +

      Backend Changes - PowerOffice Go API

      + ← Back to Documentation Home +
      + +
      + + +
      +
      +

      ⚠️ Important Notice

      +

      This page contains critical information about backend changes that may affect your integration with the PowerOffice Go API. Please read carefully.

      +
      + +
      +

      Backend Changes and Updates

      + +

      Why We Made These Changes

      +

      + We have upgraded the PowerOffice Go API backend from .NET Framework 4.7.2 to .NET 8. + This upgrade was necessary to keep our platform up to date with current technology standards and ensure we can continue + to deliver the best possible experience. +

      + +

      Benefits of the Upgrade

      +
        +
      • Performance Gains: .NET 8 offers significant performance improvements, resulting in faster response times and better scalability.
      • +
      • Enhanced Logging and Tracking: Improved observability tools allow us to better monitor, diagnose, and resolve issues quickly.
      • +
      • Modern Technology Stack: Staying current with the latest .NET platform ensures long-term support, security updates, and access to modern features.
      • +
      + +

      What This Means for You

      +

      + While we have worked to minimize disruption, you may need to make some adjustments to your integration code + to ensure compatibility with the upgraded backend. The changes required are typically minor, but they are important + to review and implement. +

      + +

      SDK

      +

      + A new version of the PowerOffice Go SDK is being prepared to support these backend changes. While the upgrade should be backward compatible, we recommend using the latest SDK version to take full advantage of the improvements and ensure compatibility with the updated backend. +

      + +

      ⚠️ Important: Temporary Exclusion Available

      +

      + If you need more time to adapt your integration, we have tools available to temporarily exclude your application + from this upgrade. However, please note that this is a temporary measure only. +

      +

      + Everyone MUST eventually migrate to the new backend. Failure to do so will result in your integration + ceasing to function when the old backend is decommissioned. We strongly encourage you to plan your migration as soon + as possible to avoid any service interruptions. +

      + +

      TL;DR

      + Access token has changed to JWT format, refresh tokens are obsolete, and request headers must be form-data or x-www-form-urlencoded. + +

      Breaking Changes

      +

      Below are the key changes that may affect your integration:

      + +

      Authentication

      +

      - Request header must be form-data or x-www-form-urlencoded

      +

      Example Request

      +
      POST /OAuth/Token HTTP/1.1
      +        Host: localhost:7239
      +        Content-Type: application/x-www-form-urlencoded
      +        Authorization: ••••••
      +        Content-Length: 29
      +        
      +        grant_type=client_credentials
      + +

      - Access token format is updated to be JSON Web Token compatible.

      The new access token contains information about the client. JWT is documented in RFC7519

      + +
      eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnb0FjdGl2ZUNsaWVudFN1YnNjcmlwdGlvbnMiOlsiQWNjb3VudGluZyIsIlRpbWVUcmFja2luZyIsIlBheXJvbGwiLCJUcmF2ZWxFeHBlbnNlIiwiUXVhbGl0eSIsIkhvbGlkYXlBbmRMZWF2ZSJdLCJnb0NsaWVudElkIjoiYWEwYWU1YWEtY2NjZS00ZTMwLWFhNjMtMDY1ZTgyY2FkZWE0IiwiZ29DbGllbnROYW1lIjoiV0lERVLDmEUgQVMiLCJnb1VzZXJJZCI6IjU1ZDUxMGM3LWJkNWUtNDQ2OS04ODhlLTc2NDViMDg0ZGMzNiIsInJvbGUiOlsiQmFua0pvdXJuYWxWb3VjaGVyX0Z1bGwiLCJCYW5rVHJhbnNmZXJfRnVsbCIsIkJsb2JfRnVsbCIsIkJ1ZGdldF9GdWxsIiwiQ2FzaEpvdXJuYWxWb3VjaGVyX0Z1bGwiLCJDbGllbnRCYW5rQWNjb3VudF9GdWxsIiwiQ29tbW9uU2VydmljZXNfRnVsbCIsIkNvbnRhY3RHcm91cF9GdWxsIiwiQ3VzdG9tRGltZW5zaW9uRGVmaW5pdGlvbl9GdWxsIiwiQ3VzdG9tRGltZW5zaW9uVmFsdWVfRnVsbCIsIkN1c3RvbWVyX0Z1bGwiLCJEZWJ0Q29sbGVjdGlvbl9GdWxsIiwiRGVwYXJ0bWVudF9GdWxsIiwiRG9jdW1lbnRzX0Z1bGwiLCJFbXBsb3llZV9GdWxsIiwiRXhwZW5zZVZvdWNoZXJfRnVsbCIsIkV4dGVybmFsbHlEZWxpdmVyYWJsZUludm9pY2VfRnVsbCIsIkZhY3RvcmluZ19GdWxsIiwiRmluYW5jaW5nX0Z1bGwiLCJHZW5lcmFsTGVkZ2VyQWNjb3VudF9GdWxsIiwiSW1wb3J0X0Z1bGwiLCJJbmNvbWluZ0ludm9pY2VWb3VjaGVyX0Z1bGwiLCJJbnZvaWNlQXR0YWNobWVudF9GdWxsIiwiSm91cm5hbEVudHJ5Vm91Y2hlcl9GdWxsIiwiTG9jYXRpb25fRnVsbCIsIk1hbnVhbEpvdXJuYWxWb3VjaGVyX0Z1bGwiLCJPdXRnb2luZ0ludm9pY2VfRnVsbCIsIk91dGdvaW5nSW52b2ljZVZvdWNoZXJfRnVsbCIsIlBhcnR5QmFua0FjY291bnRfRnVsbCIsIlBhcnR5Q29udGFjdFBlcnNvbl9GdWxsIiwiUGF5cm9sbF9GdWxsIiwiUGF5cm9sbEpvdXJuYWxWb3VjaGVyX0Z1bGwiLCJQcm9kdWN0X0Z1bGwiLCJQcm9qZWN0X0Z1bGwiLCJRdWFsaXR5X0Z1bGwiLCJRdW90ZV9GdWxsIiwiUmVwb3J0aW5nX0FjY291bnRUcmFuc2FjdGlvbl9GdWxsIiwiUmVwb3J0aW5nX0N1c3RvbWVyTGVkZ2VyX0Z1bGwiLCJSZXBvcnRpbmdfSW52b2ljZUpvdXJuYWxfRnVsbCIsIlJlcG9ydGluZ19TdXBwbGllckxlZGdlcl9GdWxsIiwiUmVwb3J0aW5nX1RpbWVUcmFuc2FjdGlvbl9GdWxsIiwiUmVwb3J0aW5nX1RyaWFsQmFsYW5jZV9GdWxsIiwiUmVwb3J0aW5nX1VzYWdlX0Z1bGwiLCJTaGFyZWhvbGRlcl9GdWxsIiwiU3VwcGxpZXJfRnVsbCIsIlRpbWVUcmFja2luZ19GdWxsIiwiVUJPX0Z1bGwiLCJWb3VjaGVyRG9jdW1lbnRhdGlvbl9GdWxsIiwiWWVhckVuZEpvdXJuYWxWb3VjaGVyX0Z1bGwiXSwidW5pcXVlX25hbWUiOiI1NWQ1MTBjNy1iZDVlLTQ0NjktODg4ZS03NjQ1YjA4NGRjMzYiLCJuYmYiOjE3NjM5OTIzNDcsImV4cCI6MTc2Mzk5MzU0NywiaWF0IjoxNzYzOTkyMzQ3LCJpc3MiOiJodHRwczovL3Rlc3QtZ28tYXBwLWFwaXYyYXV0aG9yaXphdGlvbi1ldXJ3LmF6dXJld2Vic2l0ZXMubmV0LyIsImF1ZCI6Imh0dHBzOi8vdGVzdC1nby1hcHAtYXBpdjItZXVydy5henVyZXdlYnNpdGVzLm5ldC8ifQ.n8Ipte7rIQ3M9DtpzXjbEM-9KjqS9-l63gK4fdwvqws
      + +

      - Access token time to live is expanded from 10 minutes (600 seconds) to 20 minutes (1200 seconds).

      + +

      - Refresh token is obsolete.

      +

      With the new access token format, refresh tokens are no longer necessary. Clients should request a new access token using the client credentials flow when the current token expires.

      + +

      Passing empty strings may cause validation errors like with an error field is required. Mitigiate this by removing the parameter from the request.

      +

      Empty string

      +

      Passing empty strings may cause validation errors like with an error field is required. Mitigiate this by removing the parameter from the request.

      +
      /Reporting/CustomerBalance?toDate=2025-12-31
      +        {
      +        "data": {
      +            "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
      +            "title": "One or more validation errors occurred.",
      +            "status": 400,
      +            "errors": {
      +            "contactGroup": [
      +                "The contactGroup field is required."
      +            ]
      +            },
      +                "traceId": "00-63aa14de6f41faf5361bb676b0f72637-e4915696ccd8d029-01"
      +            },
      +        "success": true
      +        }
      +

      Decimals with the value 0.0 may now be returned as 0

      +

      Number formatting

      + Decimals with the value 0.0 may now be returned as 0 + +
      + +

      Contact

      +

      If you have any questions or feedback in regards to the changes, please contact us at go-api@poweroffice.no.

      +
      + + + + diff --git a/src/App.jsx b/src/App.jsx index e471ddb..174bfa9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import Header from './components/Header'; import Hero from './components/Hero'; +import BackendChangesNotice from './components/BackendChangesNotice'; import GettingStarted from './components/GettingStarted'; import ResourceCards from './components/ResourceCards'; @@ -27,6 +28,7 @@ function App() {
      +
      diff --git a/src/components/BackendChangesNotice.jsx b/src/components/BackendChangesNotice.jsx new file mode 100644 index 0000000..8f3e500 --- /dev/null +++ b/src/components/BackendChangesNotice.jsx @@ -0,0 +1,27 @@ +import React from 'react'; + +const BackendChangesNotice = () => { + return ( +
      +
      +
      + + + +
      +
      +

      Important Changes

      +

      + We have important updates regarding our backend that may include breaking changes. + Please review this information before continuing with your integration. +

      + + View changes + +
      +
      +
      + ); +}; + +export default BackendChangesNotice; diff --git a/src/index.css b/src/index.css index c3b99df..db9e6e1 100644 --- a/src/index.css +++ b/src/index.css @@ -331,6 +331,84 @@ tbody tr:hover { text-decoration: underline; } +/* Backend Changes Notice */ +.backend-changes-notice { + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + border: 2px solid #f59e0b; + border-radius: 12px; + margin: 2rem auto; + max-width: 1200px; + padding: 2rem; + box-shadow: var(--shadow); +} + +.dark-mode .backend-changes-notice { + background: linear-gradient(135deg, #422006 0%, #713f12 100%); + border-color: #f59e0b; +} + +.notice-container { + display: flex; + gap: 1.5rem; + align-items: flex-start; +} + +.notice-icon { + flex-shrink: 0; + width: 48px; + height: 48px; + color: #f59e0b; +} + +.notice-icon svg { + width: 100%; + height: 100%; + display: block; +} + +.notice-content { + flex: 1; +} + +.notice-content h2 { + color: #78350f; + font-size: 1.5rem; + margin-bottom: 0.75rem; + font-weight: 700; +} + +.dark-mode .notice-content h2 { + color: #fbbf24; +} + +.notice-content p { + color: #78350f; + margin-bottom: 1.25rem; + line-height: 1.6; +} + +.dark-mode .notice-content p { + color: #fde68a; +} + +.notice-button { + display: inline-block; + background: #f59e0b; + color: #ffffff; + padding: 0.75rem 1.5rem; + border-radius: 8px; + text-decoration: none; + font-weight: 600; + transition: all 0.3s ease; + box-shadow: 0 2px 4px rgba(245, 158, 11, 0.3); +} + +.notice-button:hover { + background: #d97706; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(245, 158, 11, 0.4); +} + /* Skybox Background */ .skybox { position: fixed; @@ -411,4 +489,19 @@ tbody tr:hover { .resources-container { grid-template-columns: 1fr; } + + .backend-changes-notice { + margin: 1rem; + padding: 1.5rem; + } + + .notice-container { + flex-direction: column; + text-align: center; + align-items: center; + } + + .notice-content h2 { + font-size: 1.25rem; + } } From 98b1dd67a6467d5261ab52cff74a25e063284af3 Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Mon, 24 Nov 2025 16:21:59 +0100 Subject: [PATCH 5/7] added css to make menu on left in docs navigation adjustable in width. --- api-docs.html | 0 backend-changes.html | 4 +-- docs/css/main.css | 28 ++++++++++++++-- docs/index.html | 1 + docs/scripts/main.js | 66 +++++++++++++++++++++++++++++++++++++- poweroffice-api-index.html | 0 vite.config.js | 1 + 7 files changed, 94 insertions(+), 6 deletions(-) delete mode 100644 api-docs.html delete mode 100644 poweroffice-api-index.html diff --git a/api-docs.html b/api-docs.html deleted file mode 100644 index e69de29..0000000 diff --git a/backend-changes.html b/backend-changes.html index 84e46cb..ac3d4ba 100644 --- a/backend-changes.html +++ b/backend-changes.html @@ -325,10 +325,8 @@

      Empty string

      }, "success": true } -

      Decimals with the value 0.0 may now be returned as 0

      Number formatting

      - Decimals with the value 0.0 may now be returned as 0 - +

      Decimals with the value 0.0 may now be returned as 0

      Contact

      diff --git a/docs/css/main.css b/docs/css/main.css index 837a93a..6fc091c 100644 --- a/docs/css/main.css +++ b/docs/css/main.css @@ -67,12 +67,14 @@ body.dark-mode h6 { #leftmenu { position: fixed; - width: 250px; + width: 310px; height: 100%; overflow: scroll; white-space: nowrap; background-color: #f1f1f1; transition: background-color 0.3s ease; + min-width: 200px; + max-width: 600px; } body.dark-mode #leftmenu { @@ -91,13 +93,35 @@ body.dark-mode #leftmenu { background-color: #f1f1f1; } +#resize-handle { + position: fixed; + left: 310px; + top: 0; + width: 5px; + height: 100%; + cursor: col-resize; + background-color: transparent; + z-index: 1000; + transition: background-color 0.2s ease; +} + +#resize-handle:hover, +#resize-handle.dragging { + background-color: #ea802c; +} + +body.dark-mode #resize-handle:hover, +body.dark-mode #resize-handle.dragging { + background-color: #fa903c; +} + #splitter { width: 10px; height: 100% } .content { - margin-left: 250px; + margin-left: 310px; overflow: auto; padding-left: 20px; padding-right: 20px; diff --git a/docs/index.html b/docs/index.html index ebcdee6..e66e96b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1035,6 +1035,7 @@
+
\ No newline at end of file diff --git a/docs/scripts/main.js b/docs/scripts/main.js index ab2cd1f..18539b6 100644 --- a/docs/scripts/main.js +++ b/docs/scripts/main.js @@ -18,11 +18,75 @@ function initDarkMode() { } } +// Sidebar resize functionality +function initSidebarResize() { + const leftMenu = document.getElementById('leftmenu'); + const resizeHandle = document.getElementById('resize-handle'); + const content = document.querySelector('.content'); + + if (!leftMenu || !resizeHandle || !content) return; + + // Load saved width from localStorage + const savedWidth = localStorage.getItem('sidebarWidth'); + if (savedWidth) { + const width = parseInt(savedWidth); + leftMenu.style.width = width + 'px'; + resizeHandle.style.left = width + 'px'; + content.style.marginLeft = width + 'px'; + } + + let isResizing = false; + let startX; + let startWidth; + + resizeHandle.addEventListener('mousedown', function(e) { + isResizing = true; + startX = e.clientX; + startWidth = leftMenu.offsetWidth; + resizeHandle.classList.add('dragging'); + document.body.style.cursor = 'col-resize'; + document.body.style.userSelect = 'none'; + e.preventDefault(); + }); + + document.addEventListener('mousemove', function(e) { + if (!isResizing) return; + + const delta = e.clientX - startX; + let newWidth = startWidth + delta; + + // Enforce min/max width + const minWidth = 200; + const maxWidth = 600; + newWidth = Math.max(minWidth, Math.min(maxWidth, newWidth)); + + leftMenu.style.width = newWidth + 'px'; + resizeHandle.style.left = newWidth + 'px'; + content.style.marginLeft = newWidth + 'px'; + }); + + document.addEventListener('mouseup', function() { + if (isResizing) { + isResizing = false; + resizeHandle.classList.remove('dragging'); + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + + // Save the new width to localStorage + localStorage.setItem('sidebarWidth', leftMenu.offsetWidth); + } + }); +} + // Initialize on page load if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initDarkMode); + document.addEventListener('DOMContentLoaded', function() { + initDarkMode(); + initSidebarResize(); + }); } else { initDarkMode(); + initSidebarResize(); } godocs = { diff --git a/poweroffice-api-index.html b/poweroffice-api-index.html deleted file mode 100644 index e69de29..0000000 diff --git a/vite.config.js b/vite.config.js index 30e7f2b..256c270 100644 --- a/vite.config.js +++ b/vite.config.js @@ -38,6 +38,7 @@ const copyStaticFilesPlugin = () => ({ const staticFiles = [ 'robots.txt', 'api-docs.html', + 'backend-changes.html', 'poweroffice-api-index.html', 'poweroffice-api.json', 'bakgrunn_skyer.png', From 7f17fe06931398e8a34dcbd32b6ba5d6e972cb4a Mon Sep 17 00:00:00 2001 From: Kristoffer Dahl Date: Wed, 26 Nov 2025 09:20:25 +0100 Subject: [PATCH 6/7] Update backend-changes.html typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- backend-changes.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend-changes.html b/backend-changes.html index ac3d4ba..93e3890 100644 --- a/backend-changes.html +++ b/backend-changes.html @@ -307,9 +307,9 @@

- Access token time to live is expanded from 10 minutes (600 seconds) to 20

- Refresh token is obsolete.

With the new access token format, refresh tokens are no longer necessary. Clients should request a new access token using the client credentials flow when the current token expires.

-

Passing empty strings may cause validation errors like with an error field is required. Mitigiate this by removing the parameter from the request.

+

Passing empty strings may cause validation errors like with an error field is required. Mitigate this by removing the parameter from the request.

Empty string

-

Passing empty strings may cause validation errors like with an error field is required. Mitigiate this by removing the parameter from the request.

+

Passing empty strings may cause validation errors like with an error field is required. Mitigate this by removing the parameter from the request.

/Reporting/CustomerBalance?toDate=2025-12-31
         {
         "data": {

From c1c5e9b21e10485be1ad56f3a271dc9a10dc981d Mon Sep 17 00:00:00 2001
From: Kristoffer Dahl 
Date: Wed, 26 Nov 2025 09:20:43 +0100
Subject: [PATCH 7/7] Update vite.config.js

typo

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
 vite.config.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vite.config.js b/vite.config.js
index 256c270..1c42ef8 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -16,7 +16,7 @@ const copyStaticFilesPlugin = () => ({
       join(process.cwd(), 'openapi', 'swagger.json'),
       join(openapiDestDir, 'swagger.json')
     )
-    console.log('✓ Copied swagger.jso to dist/openapi')
+    console.log('✓ Copied swagger.json to dist/openapi')
     
     // Copy docs folder
     const docsSource = join(process.cwd(), 'docs')