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/api-docs.html b/api-docs.html deleted file mode 100644 index e69de29..0000000 diff --git a/backend-changes.html b/backend-changes.html new file mode 100644 index 0000000..93e3890 --- /dev/null +++ b/backend-changes.html @@ -0,0 +1,365 @@ + + + + + + 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

+ + +

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. 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. Mitigate 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
+        }
+

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/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 477f80b..6fc091c 100644 --- a/docs/css/main.css +++ b/docs/css/main.css @@ -2,19 +2,83 @@ 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 { 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 { + background-color: #18181b; } #leftmenu::-webkit-scrollbar { @@ -29,18 +93,93 @@ body { 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; } +.docs-logo { + padding: 15px; + 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 { + height: 35px; + 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; } @@ -60,12 +199,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 02c3483..e66e96b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -12,6 +12,32 @@
+
+
\ No newline at end of file diff --git a/docs/scripts/main.js b/docs/scripts/main.js index 4c03f49..18539b6 100644 --- a/docs/scripts/main.js +++ b/docs/scripts/main.js @@ -1,4 +1,95 @@ -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'; + }); + } +} + +// 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', function() { + initDarkMode(); + initSidebarResize(); + }); +} else { + initDarkMode(); + initSidebarResize(); +} + +godocs = { mainUrl: 'Welcome.md', reloadContent() { 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 -

-
-
-
- -
- 1 Register Your Application -
- 2 Read Documentation -
- 3 Start Coding -
- - -
- - - - -
- Support
- Issue Tracker
- Contact Us
-
-
-
- - - -
- + + + + + + 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/poweroffice-api-index.html b/poweroffice-api-index.html deleted file mode 100644 index e69de29..0000000 diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..174bfa9 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,39 @@ +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'; + +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 ( +
+
+ + + + +
+
+ ); +} + +export default 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/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..26674df --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,44 @@ +import React from 'react'; + +const Header = ({ darkMode, toggleDarkMode }) => { + 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..db9e6e1 --- /dev/null +++ b/src/index.css @@ -0,0 +1,507 @@ +* { + 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); +} + +.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; + 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); +} + +.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%); + padding: 4rem 2rem; + position: relative; +} + +.dark-mode .hero { + background: linear-gradient(135deg, #18181b 0%, #27272a 100%); +} + +.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; +} + +/* 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; + 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; +} + +/* 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; + 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; +} + +.dark-mode .skybox { + opacity: 0.1; + filter: brightness(0.6); +} + +/* 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; + } + + .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; + } +} 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( + + + +); diff --git a/vite.config.js b/vite.config.js index 8a3119d..1c42ef8 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.json to dist/openapi') // Copy docs folder const docsSource = join(process.cwd(), 'docs') @@ -42,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',