-
-
-
-
- @{{ username }}
-
-
+
+
+ {{ userText }}
+
\ No newline at end of file
diff --git a/src/components/ColoredHeader.vue b/src/components/ColoredHeader.vue
index 5ca412e..d61999b 100644
--- a/src/components/ColoredHeader.vue
+++ b/src/components/ColoredHeader.vue
@@ -1,5 +1,5 @@
-
+
{{ character.letter }}
@@ -58,21 +58,28 @@ const colorIncrement: Color = {
let currentColor = hexToRgb(fromHex);
for (const character of props.text) {
+ if (character.trim() === '') {
+ characters.value.push({
+ letter: character,
+ color: undefined
+ });
+ continue;
+ }
characters.value.push({
letter: character,
- color: character.trim() !== '' ? currentColor : undefined
+ color: currentColor
});
- if (character.trim() !== '') {
- currentColor = {
- R: currentColor.R + colorIncrement.R,
- G: currentColor.G + colorIncrement.G,
- B: currentColor.B + colorIncrement.B,
- };
- }
+ currentColor = {
+ R: currentColor.R + colorIncrement.R,
+ G: currentColor.G + colorIncrement.G,
+ B: currentColor.B + colorIncrement.B,
+ };
}
\ No newline at end of file
diff --git a/src/components/IssueContainer.vue b/src/components/IssueContainer.vue
index f7d8c28..b4e4ede 100644
--- a/src/components/IssueContainer.vue
+++ b/src/components/IssueContainer.vue
@@ -1,23 +1,44 @@
-
- @{{ issue.telegram_handle }}
+
+ {{ truncateWallet(issue.creator) }}
- {{ issue.summary }}
+
diff --git a/src/components/LoadingSpinner.vue b/src/components/LoadingSpinner.vue
index 5f7043e..c998e6d 100644
--- a/src/components/LoadingSpinner.vue
+++ b/src/components/LoadingSpinner.vue
@@ -11,19 +11,19 @@ const props = defineProps<{ identifier: string }>();
diff --git a/src/components/PostBuilder.vue b/src/components/PostBuilder.vue
index 4ad7c47..bdb0f4d 100644
--- a/src/components/PostBuilder.vue
+++ b/src/components/PostBuilder.vue
@@ -1,42 +1,123 @@
-
+
+
+
+ B
+
+
+ I
+
+
+ U
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue
new file mode 100644
index 0000000..365651f
--- /dev/null
+++ b/src/components/Sidebar.vue
@@ -0,0 +1,73 @@
+
+
+
+
+ Add Issue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/VotingControls.vue b/src/components/VotingControls.vue
new file mode 100644
index 0000000..1ff7cbb
--- /dev/null
+++ b/src/components/VotingControls.vue
@@ -0,0 +1,65 @@
+
+
+
+
+ Approve
+
+
+
+ Reject
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/Abstain.vue b/src/components/icons/Abstain.vue
new file mode 100644
index 0000000..fe602e2
--- /dev/null
+++ b/src/components/icons/Abstain.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/Arrow.vue b/src/components/icons/Arrow.vue
new file mode 100644
index 0000000..120e833
--- /dev/null
+++ b/src/components/icons/Arrow.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/Cancel.vue b/src/components/icons/Cancel.vue
new file mode 100644
index 0000000..757f61c
--- /dev/null
+++ b/src/components/icons/Cancel.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/Confirm.vue b/src/components/icons/Confirm.vue
new file mode 100644
index 0000000..b60b7ff
--- /dev/null
+++ b/src/components/icons/Confirm.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/Menu.vue b/src/components/icons/Menu.vue
new file mode 100644
index 0000000..60825a2
--- /dev/null
+++ b/src/components/icons/Menu.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/icons/ThumbsDown.vue b/src/components/icons/ThumbsDown.vue
index 8560b94..0a2858b 100644
--- a/src/components/icons/ThumbsDown.vue
+++ b/src/components/icons/ThumbsDown.vue
@@ -1,7 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/components/icons/ThumbsUp.vue b/src/components/icons/ThumbsUp.vue
index 75da676..8e2fede 100644
--- a/src/components/icons/ThumbsUp.vue
+++ b/src/components/icons/ThumbsUp.vue
@@ -1,7 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/components/modals/AmendmentModal.vue b/src/components/modals/AmendmentModal.vue
new file mode 100644
index 0000000..724be29
--- /dev/null
+++ b/src/components/modals/AmendmentModal.vue
@@ -0,0 +1,54 @@
+
+
+
+
+ {{ item.value }}
+
+
+
+
+
+ {{ item.value }}
+
+
+ {{ item.value }}
+
+
+ {{ item.value }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/modals/FilterModal.vue b/src/components/modals/FilterModal.vue
index d761cf6..30360ef 100644
--- a/src/components/modals/FilterModal.vue
+++ b/src/components/modals/FilterModal.vue
@@ -1,17 +1,17 @@
-
+
diff --git a/src/components/modals/IssueModal.vue b/src/components/modals/IssueModal.vue
index 75173e1..d239e26 100644
--- a/src/components/modals/IssueModal.vue
+++ b/src/components/modals/IssueModal.vue
@@ -1,11 +1,11 @@
-
+
@@ -13,32 +13,24 @@
diff --git a/src/components/modals/Modal.vue b/src/components/modals/Modal.vue
index 9cf850a..08984a6 100644
--- a/src/components/modals/Modal.vue
+++ b/src/components/modals/Modal.vue
@@ -1,22 +1,24 @@
-
-
+
+
×
-
-
+
+
-
- {{ header }}
-
+
+ {{ header }}
+
-
-
+
+
+
+
- Submit
+ {{ buttonText }}
@@ -25,13 +27,16 @@
diff --git a/src/composables/useFetch.ts b/src/composables/useFetch.ts
index 63a50c5..790e898 100644
--- a/src/composables/useFetch.ts
+++ b/src/composables/useFetch.ts
@@ -1,20 +1,70 @@
+/**
+ * @deprecated This method is deprecated.
+ */
export const useFetch = () => {
- const get = async
(endpoint: string) => {
- const data = await fetch(`/api${ endpoint }`, {
- method: 'GET',
+ interface FetchConfig {
+ baseUrl: string;
+ headers: HeadersInit;
+ }
+
+ interface ApiResponse {
+ data: T;
+ status: number;
+ }
+
+ const DEFAULT_CONFIG: FetchConfig = {
+ baseUrl: '/api/v1',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ };
+
+ const buildQueryString = (params?: Record): string => {
+ if (!params) return '';
+ return '?' + Object.entries(params)
+ .map(([key, value]) => `${key}=${value}`)
+ .join('&');
+ };
+
+ /**
+ * @deprecated This function is deprecated.
+ */
+ const createRequest = async (
+ endpoint: string,
+ method: 'GET' | 'POST',
+ options: RequestInit = {}
+ ): Promise> => {
+ const response = await fetch(`${DEFAULT_CONFIG.baseUrl}${endpoint}`, {
+ method,
mode: 'cors',
- headers: new Headers({ 'Content-Type': 'application/json' })
+ headers: new Headers(DEFAULT_CONFIG.headers),
+ ...options
});
- return await data.json() as T;
+
+ const data = await response.json();
+ return {data, status: response.status};
};
- const post = async (endpoint: string, payload: T) => {
- const data = await fetch(`/api${ endpoint }`, {
- method: 'POST',
- mode: 'cors',
- headers: { 'Content-Type': 'application/json' },
+
+ /**
+ * @deprecated This function is deprecated.
+ */
+ const get = async (
+ endpoint: string,
+ queryParams?: Record
+ ): Promise => {
+ const query = buildQueryString(queryParams);
+ const response = await createRequest(`${endpoint}${query}`, 'GET');
+ return response.data;
+ };
+
+ /**
+ * @deprecated This function is deprecated.
+ */
+ const post = async (endpoint: string, payload: T): Promise => {
+ const response = await createRequest(endpoint, 'POST', {
body: JSON.stringify(payload)
});
- return await data.json() as U;
+ return response.data;
};
return { get, post }
diff --git a/src/composables/useModal.ts b/src/composables/useModal.ts
index a79276c..0dfd1cc 100644
--- a/src/composables/useModal.ts
+++ b/src/composables/useModal.ts
@@ -2,8 +2,16 @@ import { ref } from 'vue'
export class ContentModal {}
+export type ModalContentFormat = 'heading' | 'paragraph' | 'subheading' | 'small' | 'chip';
+export type ModalContentItem = {
+ key: string;
+ value: string | number;
+ format?: ModalContentFormat;
+};
+
const visible = ref(false);
const modalContent = ref(null);
+const contentItems = ref([]);
export const useModal = () => {
const getVisibility = () => visible.value;
@@ -16,5 +24,48 @@ export const useModal = () => {
modalContent.value = null;
visible.value = false;
}
- return { getVisibility, getModalContent, show, hide };
+
+ // Set or update a single content entry (by key)
+ const setContent = (key: string, value: string | number, format?: ModalContentFormat) => {
+ const idx = contentItems.value.findIndex(i => i.key === key);
+ const item: ModalContentItem = { key, value, format };
+ if (idx >= 0) contentItems.value.splice(idx, 1, item);
+ else contentItems.value.push(item);
+ }
+
+ // Add content entries
+ function addContent(item: ModalContentItem): void;
+ function addContent(items: ModalContentItem[]): void;
+ function addContent(arg: ModalContentItem | ModalContentItem[]) {
+ if (Array.isArray(arg)) {
+ for (const it of arg) contentItems.value.push(it);
+ } else if (arg && typeof arg === 'object') {
+ contentItems.value.push(arg);
+ }
+ }
+
+ // Remove a content entry by key
+ const removeContent = (key: string) => {
+ const idx = contentItems.value.findIndex(i => i.key === key);
+ if (idx >= 0) contentItems.value.splice(idx, 1);
+ }
+
+ // Clear all dynamic content
+ const clearContent = () => {
+ contentItems.value = [];
+ }
+
+ const getContent = (key: string) => contentItems.value.find(i => i.key === key);
+ const getAllContent = () => contentItems.value;
+
+ // Convenience: set amendment content with formatting centralized here
+ const setAmendmentContent = (name: string, status: string, body: string) => {
+ contentItems.value = [
+ { key: 'name', value: name, format: 'heading' },
+ { key: 'status', value: status, format: 'chip' },
+ { key: 'body', value: body, format: 'paragraph' },
+ ];
+ }
+
+ return { getVisibility, getModalContent, setContent, addContent, removeContent, clearContent, getContent, getAllContent, setAmendmentContent, show, hide };
}
diff --git a/src/composables/useSession.ts b/src/composables/useSession.ts
index e8ae130..67e2596 100644
--- a/src/composables/useSession.ts
+++ b/src/composables/useSession.ts
@@ -1,46 +1,9 @@
-import { ref } from "vue";
-import { useCookies } from '@vueuse/integrations/useCookies'
-import { useFetch } from "./useFetch.ts";
-import { GetUsernameResponse } from "../types/user.ts";
-
-const cookies = useCookies(['session']);
-const { get } = useFetch();
-
-const sessionId = ref();
-const username = ref();
+import freighter from '@stellar/freighter-api';
export const useSession = () => {
- const setSessionId = (id: string) => {
- cookies.set('session', id);
- sessionId.value = id;
- }
-
- const getSessionId = (): string | undefined => sessionId.value;
-
- const initializeSessionId = (): void => {
- if (!sessionId.value) {
- const cookieSessionId = cookies.get('session');
- if (cookieSessionId) {
- setSessionId(cookieSessionId);
- }
- }
- };
-
- const setUsername = (name: string | undefined): void => {
- username.value = name;
- }
-
- const getUsername = (): string | undefined => username.value;
-
- const initializeUsername = async () => {
- if (!username.value) {
- const cookieSessionId = cookies.get('session');
- if (cookieSessionId) {
- const username = await get(`/sessions/get_username?session_id=${ cookieSessionId }`);
- setUsername(username.username);
- }
- }
+ const hasFreighter = async () => {
+ return freighter.isConnected();
}
- return { initializeSessionId, setSessionId, getSessionId, setUsername, getUsername, initializeUsername };
+ return { hasFreighter };
}
\ No newline at end of file
diff --git a/src/generated/typescript/core/ApiError.ts b/src/generated/typescript/core/ApiError.ts
new file mode 100644
index 0000000..ec7b16a
--- /dev/null
+++ b/src/generated/typescript/core/ApiError.ts
@@ -0,0 +1,25 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+
+export class ApiError extends Error {
+ public readonly url: string;
+ public readonly status: number;
+ public readonly statusText: string;
+ public readonly body: any;
+ public readonly request: ApiRequestOptions;
+
+ constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
+ super(message);
+
+ this.name = 'ApiError';
+ this.url = response.url;
+ this.status = response.status;
+ this.statusText = response.statusText;
+ this.body = response.body;
+ this.request = request;
+ }
+}
diff --git a/src/generated/typescript/core/ApiRequestOptions.ts b/src/generated/typescript/core/ApiRequestOptions.ts
new file mode 100644
index 0000000..93143c3
--- /dev/null
+++ b/src/generated/typescript/core/ApiRequestOptions.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiRequestOptions = {
+ readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
+ readonly url: string;
+ readonly path?: Record;
+ readonly cookies?: Record;
+ readonly headers?: Record;
+ readonly query?: Record;
+ readonly formData?: Record;
+ readonly body?: any;
+ readonly mediaType?: string;
+ readonly responseHeader?: string;
+ readonly errors?: Record;
+};
diff --git a/src/generated/typescript/core/ApiResult.ts b/src/generated/typescript/core/ApiResult.ts
new file mode 100644
index 0000000..ee1126e
--- /dev/null
+++ b/src/generated/typescript/core/ApiResult.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiResult = {
+ readonly url: string;
+ readonly ok: boolean;
+ readonly status: number;
+ readonly statusText: string;
+ readonly body: any;
+};
diff --git a/src/generated/typescript/core/CancelablePromise.ts b/src/generated/typescript/core/CancelablePromise.ts
new file mode 100644
index 0000000..d70de92
--- /dev/null
+++ b/src/generated/typescript/core/CancelablePromise.ts
@@ -0,0 +1,131 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export class CancelError extends Error {
+
+ constructor(message: string) {
+ super(message);
+ this.name = 'CancelError';
+ }
+
+ public get isCancelled(): boolean {
+ return true;
+ }
+}
+
+export interface OnCancel {
+ readonly isResolved: boolean;
+ readonly isRejected: boolean;
+ readonly isCancelled: boolean;
+
+ (cancelHandler: () => void): void;
+}
+
+export class CancelablePromise implements Promise {
+ #isResolved: boolean;
+ #isRejected: boolean;
+ #isCancelled: boolean;
+ readonly #cancelHandlers: (() => void)[];
+ readonly #promise: Promise;
+ #resolve?: (value: T | PromiseLike) => void;
+ #reject?: (reason?: any) => void;
+
+ constructor(
+ executor: (
+ resolve: (value: T | PromiseLike) => void,
+ reject: (reason?: any) => void,
+ onCancel: OnCancel
+ ) => void
+ ) {
+ this.#isResolved = false;
+ this.#isRejected = false;
+ this.#isCancelled = false;
+ this.#cancelHandlers = [];
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
+
+ const onResolve = (value: T | PromiseLike): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isResolved = true;
+ if (this.#resolve) this.#resolve(value);
+ };
+
+ const onReject = (reason?: any): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isRejected = true;
+ if (this.#reject) this.#reject(reason);
+ };
+
+ const onCancel = (cancelHandler: () => void): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#cancelHandlers.push(cancelHandler);
+ };
+
+ Object.defineProperty(onCancel, 'isResolved', {
+ get: (): boolean => this.#isResolved,
+ });
+
+ Object.defineProperty(onCancel, 'isRejected', {
+ get: (): boolean => this.#isRejected,
+ });
+
+ Object.defineProperty(onCancel, 'isCancelled', {
+ get: (): boolean => this.#isCancelled,
+ });
+
+ return executor(onResolve, onReject, onCancel as OnCancel);
+ });
+ }
+
+ get [Symbol.toStringTag]() {
+ return "Cancellable Promise";
+ }
+
+ public then(
+ onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null,
+ onRejected?: ((reason: any) => TResult2 | PromiseLike) | null
+ ): Promise {
+ return this.#promise.then(onFulfilled, onRejected);
+ }
+
+ public catch(
+ onRejected?: ((reason: any) => TResult | PromiseLike) | null
+ ): Promise {
+ return this.#promise.catch(onRejected);
+ }
+
+ public finally(onFinally?: (() => void) | null): Promise {
+ return this.#promise.finally(onFinally);
+ }
+
+ public cancel(): void {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isCancelled = true;
+ if (this.#cancelHandlers.length) {
+ try {
+ for (const cancelHandler of this.#cancelHandlers) {
+ cancelHandler();
+ }
+ } catch (error) {
+ console.warn('Cancellation threw an error', error);
+ return;
+ }
+ }
+ this.#cancelHandlers.length = 0;
+ if (this.#reject) this.#reject(new CancelError('Request aborted'));
+ }
+
+ public get isCancelled(): boolean {
+ return this.#isCancelled;
+ }
+}
diff --git a/src/generated/typescript/core/OpenAPI.ts b/src/generated/typescript/core/OpenAPI.ts
new file mode 100644
index 0000000..061313b
--- /dev/null
+++ b/src/generated/typescript/core/OpenAPI.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+type Headers = Record;
+
+export type OpenAPIConfig = {
+ BASE: string;
+ VERSION: string;
+ WITH_CREDENTIALS: boolean;
+ CREDENTIALS: 'include' | 'omit' | 'same-origin';
+ TOKEN?: string | Resolver | undefined;
+ USERNAME?: string | Resolver | undefined;
+ PASSWORD?: string | Resolver | undefined;
+ HEADERS?: Headers | Resolver | undefined;
+ ENCODE_PATH?: ((path: string) => string) | undefined;
+};
+
+export const OpenAPI: OpenAPIConfig = {
+ BASE: 'http://localhost:7300',
+ VERSION: '1.0.0',
+ WITH_CREDENTIALS: false,
+ CREDENTIALS: 'include',
+ TOKEN: undefined,
+ USERNAME: undefined,
+ PASSWORD: undefined,
+ HEADERS: undefined,
+ ENCODE_PATH: undefined,
+};
diff --git a/src/generated/typescript/core/request.ts b/src/generated/typescript/core/request.ts
new file mode 100644
index 0000000..f83d711
--- /dev/null
+++ b/src/generated/typescript/core/request.ts
@@ -0,0 +1,322 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import { ApiError } from './ApiError';
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+import { CancelablePromise } from './CancelablePromise';
+import type { OnCancel } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+
+export const isDefined = (value: T | null | undefined): value is Exclude => {
+ return value !== undefined && value !== null;
+};
+
+export const isString = (value: any): value is string => {
+ return typeof value === 'string';
+};
+
+export const isStringWithValue = (value: any): value is string => {
+ return isString(value) && value !== '';
+};
+
+export const isBlob = (value: any): value is Blob => {
+ return (
+ typeof value === 'object' &&
+ typeof value.type === 'string' &&
+ typeof value.stream === 'function' &&
+ typeof value.arrayBuffer === 'function' &&
+ typeof value.constructor === 'function' &&
+ typeof value.constructor.name === 'string' &&
+ /^(Blob|File)$/.test(value.constructor.name) &&
+ /^(Blob|File)$/.test(value[Symbol.toStringTag])
+ );
+};
+
+export const isFormData = (value: any): value is FormData => {
+ return value instanceof FormData;
+};
+
+export const base64 = (str: string): string => {
+ try {
+ return btoa(str);
+ } catch (err) {
+ // @ts-ignore
+ return Buffer.from(str).toString('base64');
+ }
+};
+
+export const getQueryString = (params: Record): string => {
+ const qs: string[] = [];
+
+ const append = (key: string, value: any) => {
+ qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
+ };
+
+ const process = (key: string, value: any) => {
+ if (isDefined(value)) {
+ if (Array.isArray(value)) {
+ value.forEach(v => {
+ process(key, v);
+ });
+ } else if (typeof value === 'object') {
+ Object.entries(value).forEach(([k, v]) => {
+ process(`${key}[${k}]`, v);
+ });
+ } else {
+ append(key, value);
+ }
+ }
+ };
+
+ Object.entries(params).forEach(([key, value]) => {
+ process(key, value);
+ });
+
+ if (qs.length > 0) {
+ return `?${qs.join('&')}`;
+ }
+
+ return '';
+};
+
+const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
+ const encoder = config.ENCODE_PATH || encodeURI;
+
+ const path = options.url
+ .replace('{api-version}', config.VERSION)
+ .replace(/{(.*?)}/g, (substring: string, group: string) => {
+ if (options.path?.hasOwnProperty(group)) {
+ return encoder(String(options.path[group]));
+ }
+ return substring;
+ });
+
+ const url = `${config.BASE}${path}`;
+ if (options.query) {
+ return `${url}${getQueryString(options.query)}`;
+ }
+ return url;
+};
+
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+ if (options.formData) {
+ const formData = new FormData();
+
+ const process = (key: string, value: any) => {
+ if (isString(value) || isBlob(value)) {
+ formData.append(key, value);
+ } else {
+ formData.append(key, JSON.stringify(value));
+ }
+ };
+
+ Object.entries(options.formData)
+ .filter(([_, value]) => isDefined(value))
+ .forEach(([key, value]) => {
+ if (Array.isArray(value)) {
+ value.forEach(v => process(key, v));
+ } else {
+ process(key, value);
+ }
+ });
+
+ return formData;
+ }
+ return undefined;
+};
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+
+export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => {
+ if (typeof resolver === 'function') {
+ return (resolver as Resolver)(options);
+ }
+ return resolver;
+};
+
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => {
+ const [token, username, password, additionalHeaders] = await Promise.all([
+ resolve(options, config.TOKEN),
+ resolve(options, config.USERNAME),
+ resolve(options, config.PASSWORD),
+ resolve(options, config.HEADERS),
+ ]);
+
+ const headers = Object.entries({
+ Accept: 'application/json',
+ ...additionalHeaders,
+ ...options.headers,
+ })
+ .filter(([_, value]) => isDefined(value))
+ .reduce((headers, [key, value]) => ({
+ ...headers,
+ [key]: String(value),
+ }), {} as Record);
+
+ if (isStringWithValue(token)) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ if (isStringWithValue(username) && isStringWithValue(password)) {
+ const credentials = base64(`${username}:${password}`);
+ headers['Authorization'] = `Basic ${credentials}`;
+ }
+
+ if (options.body !== undefined) {
+ if (options.mediaType) {
+ headers['Content-Type'] = options.mediaType;
+ } else if (isBlob(options.body)) {
+ headers['Content-Type'] = options.body.type || 'application/octet-stream';
+ } else if (isString(options.body)) {
+ headers['Content-Type'] = 'text/plain';
+ } else if (!isFormData(options.body)) {
+ headers['Content-Type'] = 'application/json';
+ }
+ }
+
+ return new Headers(headers);
+};
+
+export const getRequestBody = (options: ApiRequestOptions): any => {
+ if (options.body !== undefined) {
+ if (options.mediaType?.includes('/json')) {
+ return JSON.stringify(options.body)
+ } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
+ return options.body;
+ } else {
+ return JSON.stringify(options.body);
+ }
+ }
+ return undefined;
+};
+
+export const sendRequest = async (
+ config: OpenAPIConfig,
+ options: ApiRequestOptions,
+ url: string,
+ body: any,
+ formData: FormData | undefined,
+ headers: Headers,
+ onCancel: OnCancel
+): Promise => {
+ const controller = new AbortController();
+
+ const request: RequestInit = {
+ headers,
+ body: body ?? formData,
+ method: options.method,
+ signal: controller.signal,
+ };
+
+ if (config.WITH_CREDENTIALS) {
+ request.credentials = config.CREDENTIALS;
+ }
+
+ onCancel(() => controller.abort());
+
+ return await fetch(url, request);
+};
+
+export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {
+ if (responseHeader) {
+ const content = response.headers.get(responseHeader);
+ if (isString(content)) {
+ return content;
+ }
+ }
+ return undefined;
+};
+
+export const getResponseBody = async (response: Response): Promise => {
+ if (response.status !== 204) {
+ try {
+ const contentType = response.headers.get('Content-Type');
+ if (contentType) {
+ const jsonTypes = ['application/json', 'application/problem+json']
+ const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));
+ if (isJSON) {
+ return await response.json();
+ } else {
+ return await response.text();
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ return undefined;
+};
+
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+ const errors: Record = {
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 500: 'Internal Server Error',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ ...options.errors,
+ }
+
+ const error = errors[result.status];
+ if (error) {
+ throw new ApiError(options, result, error);
+ }
+
+ if (!result.ok) {
+ const errorStatus = result.status ?? 'unknown';
+ const errorStatusText = result.statusText ?? 'unknown';
+ const errorBody = (() => {
+ try {
+ return JSON.stringify(result.body, null, 2);
+ } catch (e) {
+ return undefined;
+ }
+ })();
+
+ throw new ApiError(options, result,
+ `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+ );
+ }
+};
+
+/**
+ * Request method
+ * @param config The OpenAPI configuration object
+ * @param options The request options from the service
+ * @returns CancelablePromise
+ * @throws ApiError
+ */
+export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => {
+ return new CancelablePromise(async (resolve, reject, onCancel) => {
+ try {
+ const url = getUrl(config, options);
+ const formData = getFormData(options);
+ const body = getRequestBody(options);
+ const headers = await getHeaders(config, options);
+
+ if (!onCancel.isCancelled) {
+ const response = await sendRequest(config, options, url, body, formData, headers, onCancel);
+ const responseBody = await getResponseBody(response);
+ const responseHeader = getResponseHeader(response, options.responseHeader);
+
+ const result: ApiResult = {
+ url,
+ ok: response.ok,
+ status: response.status,
+ statusText: response.statusText,
+ body: responseHeader ?? responseBody,
+ };
+
+ catchErrorCodes(options, result);
+
+ resolve(result.body);
+ }
+ } catch (error) {
+ reject(error);
+ }
+ });
+};
diff --git a/src/generated/typescript/index.ts b/src/generated/typescript/index.ts
new file mode 100644
index 0000000..373df0d
--- /dev/null
+++ b/src/generated/typescript/index.ts
@@ -0,0 +1,30 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export { ApiError } from './core/ApiError';
+export { CancelablePromise, CancelError } from './core/CancelablePromise';
+export { OpenAPI } from './core/OpenAPI';
+export type { OpenAPIConfig } from './core/OpenAPI';
+
+export type { AddProposalRequest } from './models/AddProposalRequest';
+export type { FreighterLoginRequest } from './models/FreighterLoginRequest';
+export type { ListProposalsResponse } from './models/ListProposalsResponse';
+export type { LoginRequest } from './models/LoginRequest';
+export type { ProposalItem } from './models/ProposalItem';
+export type { ProposalList } from './models/ProposalList';
+export type { RegisterRequest } from './models/RegisterRequest';
+export type { VisitProposalRequest } from './models/VisitProposalRequest';
+
+export { $AddProposalRequest } from './schemas/$AddProposalRequest';
+export { $FreighterLoginRequest } from './schemas/$FreighterLoginRequest';
+export { $ListProposalsResponse } from './schemas/$ListProposalsResponse';
+export { $LoginRequest } from './schemas/$LoginRequest';
+export { $ProposalItem } from './schemas/$ProposalItem';
+export { $ProposalList } from './schemas/$ProposalList';
+export { $RegisterRequest } from './schemas/$RegisterRequest';
+export { $VisitProposalRequest } from './schemas/$VisitProposalRequest';
+
+export { AmendmentService } from './services/AmendmentService';
+export { AuthService } from './services/AuthService';
+export { ProposalService } from './services/ProposalService';
diff --git a/src/generated/typescript/models/AddProposalRequest.ts b/src/generated/typescript/models/AddProposalRequest.ts
new file mode 100644
index 0000000..24bfc6e
--- /dev/null
+++ b/src/generated/typescript/models/AddProposalRequest.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type AddProposalRequest = {
+ creator: string;
+ description: string;
+ name: string;
+};
+
diff --git a/src/generated/typescript/models/FreighterLoginRequest.ts b/src/generated/typescript/models/FreighterLoginRequest.ts
new file mode 100644
index 0000000..5fe9251
--- /dev/null
+++ b/src/generated/typescript/models/FreighterLoginRequest.ts
@@ -0,0 +1,8 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type FreighterLoginRequest = {
+ stellarAddress: string;
+};
+
diff --git a/src/generated/typescript/models/ListProposalsResponse.ts b/src/generated/typescript/models/ListProposalsResponse.ts
new file mode 100644
index 0000000..5fb48de
--- /dev/null
+++ b/src/generated/typescript/models/ListProposalsResponse.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ProposalList } from './ProposalList';
+export type ListProposalsResponse = {
+ proposals?: ProposalList;
+};
+
diff --git a/src/generated/typescript/models/LoginRequest.ts b/src/generated/typescript/models/LoginRequest.ts
new file mode 100644
index 0000000..33a82b1
--- /dev/null
+++ b/src/generated/typescript/models/LoginRequest.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type LoginRequest = {
+ password: string;
+ username: string;
+};
+
diff --git a/src/generated/typescript/models/ProposalItem.ts b/src/generated/typescript/models/ProposalItem.ts
new file mode 100644
index 0000000..3452ced
--- /dev/null
+++ b/src/generated/typescript/models/ProposalItem.ts
@@ -0,0 +1,16 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ProposalItem = {
+ cid: string;
+ createdAt?: string | null;
+ creator: string;
+ id: number;
+ isCurrent: boolean;
+ name: string;
+ previousCid?: string | null;
+ summary?: string | null;
+ updatedAt?: string | null;
+};
+
diff --git a/src/generated/typescript/models/ProposalList.ts b/src/generated/typescript/models/ProposalList.ts
new file mode 100644
index 0000000..1956b3a
--- /dev/null
+++ b/src/generated/typescript/models/ProposalList.ts
@@ -0,0 +1,6 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ProposalItem } from './ProposalItem';
+export type ProposalList = Array;
diff --git a/src/generated/typescript/models/RegisterRequest.ts b/src/generated/typescript/models/RegisterRequest.ts
new file mode 100644
index 0000000..132f726
--- /dev/null
+++ b/src/generated/typescript/models/RegisterRequest.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type RegisterRequest = {
+ email?: string | null;
+ password: string;
+ stellarAddress: string;
+ username: string;
+};
+
diff --git a/src/generated/typescript/models/VisitProposalRequest.ts b/src/generated/typescript/models/VisitProposalRequest.ts
new file mode 100644
index 0000000..53c0d87
--- /dev/null
+++ b/src/generated/typescript/models/VisitProposalRequest.ts
@@ -0,0 +1,13 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type VisitProposalRequest = {
+ method?: string;
+ path?: string;
+ proposalId: number;
+ queryString?: string;
+ referrer?: string;
+ statusCode?: number;
+};
+
diff --git a/src/generated/typescript/schemas/$AddProposalRequest.ts b/src/generated/typescript/schemas/$AddProposalRequest.ts
new file mode 100644
index 0000000..0457d84
--- /dev/null
+++ b/src/generated/typescript/schemas/$AddProposalRequest.ts
@@ -0,0 +1,20 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $AddProposalRequest = {
+ properties: {
+ creator: {
+ type: 'string',
+ isRequired: true,
+ },
+ description: {
+ type: 'string',
+ isRequired: true,
+ },
+ name: {
+ type: 'string',
+ isRequired: true,
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$FreighterLoginRequest.ts b/src/generated/typescript/schemas/$FreighterLoginRequest.ts
new file mode 100644
index 0000000..b8978e3
--- /dev/null
+++ b/src/generated/typescript/schemas/$FreighterLoginRequest.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $FreighterLoginRequest = {
+ properties: {
+ stellarAddress: {
+ type: 'string',
+ isRequired: true,
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$ListProposalsResponse.ts b/src/generated/typescript/schemas/$ListProposalsResponse.ts
new file mode 100644
index 0000000..39f5f63
--- /dev/null
+++ b/src/generated/typescript/schemas/$ListProposalsResponse.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $ListProposalsResponse = {
+ properties: {
+ proposals: {
+ type: 'ProposalList',
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$LoginRequest.ts b/src/generated/typescript/schemas/$LoginRequest.ts
new file mode 100644
index 0000000..d2fe9a0
--- /dev/null
+++ b/src/generated/typescript/schemas/$LoginRequest.ts
@@ -0,0 +1,16 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $LoginRequest = {
+ properties: {
+ password: {
+ type: 'string',
+ isRequired: true,
+ },
+ username: {
+ type: 'string',
+ isRequired: true,
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$ProposalItem.ts b/src/generated/typescript/schemas/$ProposalItem.ts
new file mode 100644
index 0000000..ab2f42a
--- /dev/null
+++ b/src/generated/typescript/schemas/$ProposalItem.ts
@@ -0,0 +1,45 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $ProposalItem = {
+ properties: {
+ cid: {
+ type: 'string',
+ isRequired: true,
+ },
+ createdAt: {
+ type: 'string',
+ isNullable: true,
+ },
+ creator: {
+ type: 'string',
+ isRequired: true,
+ },
+ id: {
+ type: 'number',
+ isRequired: true,
+ format: 'int32',
+ },
+ isCurrent: {
+ type: 'boolean',
+ isRequired: true,
+ },
+ name: {
+ type: 'string',
+ isRequired: true,
+ },
+ previousCid: {
+ type: 'string',
+ isNullable: true,
+ },
+ summary: {
+ type: 'string',
+ isNullable: true,
+ },
+ updatedAt: {
+ type: 'string',
+ isNullable: true,
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$ProposalList.ts b/src/generated/typescript/schemas/$ProposalList.ts
new file mode 100644
index 0000000..c1ef996
--- /dev/null
+++ b/src/generated/typescript/schemas/$ProposalList.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $ProposalList = {
+ type: 'array',
+ contains: {
+ type: 'ProposalItem',
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$RegisterRequest.ts b/src/generated/typescript/schemas/$RegisterRequest.ts
new file mode 100644
index 0000000..35aaae0
--- /dev/null
+++ b/src/generated/typescript/schemas/$RegisterRequest.ts
@@ -0,0 +1,24 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $RegisterRequest = {
+ properties: {
+ email: {
+ type: 'string',
+ isNullable: true,
+ },
+ password: {
+ type: 'string',
+ isRequired: true,
+ },
+ stellarAddress: {
+ type: 'string',
+ isRequired: true,
+ },
+ username: {
+ type: 'string',
+ isRequired: true,
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/schemas/$VisitProposalRequest.ts b/src/generated/typescript/schemas/$VisitProposalRequest.ts
new file mode 100644
index 0000000..fecad73
--- /dev/null
+++ b/src/generated/typescript/schemas/$VisitProposalRequest.ts
@@ -0,0 +1,29 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export const $VisitProposalRequest = {
+ properties: {
+ method: {
+ type: 'string',
+ },
+ path: {
+ type: 'string',
+ },
+ proposalId: {
+ type: 'number',
+ isRequired: true,
+ format: 'int32',
+ },
+ queryString: {
+ type: 'string',
+ },
+ referrer: {
+ type: 'string',
+ },
+ statusCode: {
+ type: 'number',
+ format: 'int32',
+ },
+ },
+} as const;
diff --git a/src/generated/typescript/services/AmendmentService.ts b/src/generated/typescript/services/AmendmentService.ts
new file mode 100644
index 0000000..71bac52
--- /dev/null
+++ b/src/generated/typescript/services/AmendmentService.ts
@@ -0,0 +1,59 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class AmendmentService {
+ /**
+ * Create an amendment
+ * @param requestBody
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static createAmendment(
+ requestBody: Record,
+ ): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/amendment/add',
+ body: requestBody,
+ mediaType: 'application/json',
+ });
+ }
+ /**
+ * Get an amendment
+ * @param id
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static getAmendment(
+ id: number,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/api/v1/amendment/get',
+ query: {
+ 'id': id,
+ },
+ });
+ }
+ /**
+ * List amendments
+ * @param proposalId
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static listAmendments(
+ proposalId: number,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/api/v1/amendments/list',
+ query: {
+ 'proposalId': proposalId,
+ },
+ });
+ }
+}
diff --git a/src/generated/typescript/services/AuthService.ts b/src/generated/typescript/services/AuthService.ts
new file mode 100644
index 0000000..6618200
--- /dev/null
+++ b/src/generated/typescript/services/AuthService.ts
@@ -0,0 +1,81 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { FreighterLoginRequest } from '../models/FreighterLoginRequest';
+import type { LoginRequest } from '../models/LoginRequest';
+import type { RegisterRequest } from '../models/RegisterRequest';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class AuthService {
+ /**
+ * Login with username/password
+ * @param requestBody
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static loginUser(
+ requestBody: LoginRequest,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/auth/login',
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 401: `Unauthorized`,
+ },
+ });
+ }
+ /**
+ * Login with Stellar (Freighter)
+ * @param requestBody
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static loginFreighter(
+ requestBody: FreighterLoginRequest,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/auth/login/freighter',
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 401: `Unauthorized`,
+ },
+ });
+ }
+ /**
+ * Get freighter login nonce
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static getFreighterNonce(): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/api/v1/auth/nonce',
+ });
+ }
+ /**
+ * Register a user
+ * @param requestBody
+ * @returns any Created
+ * @throws ApiError
+ */
+ public static registerUser(
+ requestBody: RegisterRequest,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/auth/register',
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 400: `Bad Request`,
+ 409: `Conflict`,
+ },
+ });
+ }
+}
diff --git a/src/generated/typescript/services/ProposalService.ts b/src/generated/typescript/services/ProposalService.ts
new file mode 100644
index 0000000..a2ab0ed
--- /dev/null
+++ b/src/generated/typescript/services/ProposalService.ts
@@ -0,0 +1,84 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { AddProposalRequest } from '../models/AddProposalRequest';
+import type { ListProposalsResponse } from '../models/ListProposalsResponse';
+import type { VisitProposalRequest } from '../models/VisitProposalRequest';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class ProposalService {
+ /**
+ * Create a proposal
+ * @param requestBody
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static createProposal(
+ requestBody: AddProposalRequest,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/proposal/add',
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 400: `Bad Request`,
+ },
+ });
+ }
+ /**
+ * Get a proposal by id
+ * @param id
+ * @returns any OK
+ * @throws ApiError
+ */
+ public static getProposal(
+ id: number,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/api/v1/proposal/get',
+ query: {
+ 'id': id,
+ },
+ });
+ }
+ /**
+ * Record a proposal visit
+ * @param requestBody
+ * @returns any Created
+ * @throws ApiError
+ */
+ public static recordProposalVisit(
+ requestBody: VisitProposalRequest,
+ ): CancelablePromise> {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/api/v1/proposal/visit',
+ body: requestBody,
+ mediaType: 'application/json',
+ });
+ }
+ /**
+ * List proposals
+ * @param offset
+ * @param limit
+ * @returns ListProposalsResponse OK
+ * @throws ApiError
+ */
+ public static listProposals(
+ offset: number,
+ limit: number,
+ ): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/api/v1/proposals/list',
+ query: {
+ 'offset': offset,
+ 'limit': limit,
+ },
+ });
+ }
+}
diff --git a/src/index.css b/src/index.css
index bfd8355..a87c870 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,5 +1,13 @@
-@import url('https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Gabarito:wght@400..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import "tailwindcss";
+
+@layer base {
+ input[type='number']::-webkit-outer-spin-button,
+ input[type='number']::-webkit-inner-spin-button,
+ input[type='number'] {
+ -webkit-appearance: none;
+ margin: 0;
+ -moz-appearance: textfield !important;
+ }
+}
diff --git a/src/main.ts b/src/main.ts
index 025104b..817509b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -3,6 +3,7 @@ import './index.css'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
+import './index.css'
const pinia = createPinia();
createApp(App).use(pinia).use(router).mount('#app')
diff --git a/src/types/amendment.ts b/src/types/amendment.ts
new file mode 100644
index 0000000..3f07774
--- /dev/null
+++ b/src/types/amendment.ts
@@ -0,0 +1,30 @@
+export interface Amendment {
+ id: number;
+ name: string;
+ cid: string;
+ summary?: string;
+ status: 'proposed' | 'approved' | 'withdrawn' | 'rejected';
+ creator: string;
+ isCurrent: boolean;
+ previousCid?: string;
+ createdAt: string;
+ updatedAt: string;
+ proposalId: number;
+}
+
+export interface ListAmendmentsResponse {
+ amendments: Amendment[];
+}
+
+export interface AmendmentFile {
+ name: string;
+ content: string;
+ creator: string;
+ createdAt: Date;
+ updatedAt: Date;
+ proposalId: number;
+}
+
+export interface GetAmendmentResponse {
+ amendment: AmendmentFile;
+}
\ No newline at end of file
diff --git a/src/types/color.ts b/src/types/color.ts
index 4b47b44..572b0d7 100644
--- a/src/types/color.ts
+++ b/src/types/color.ts
@@ -1,5 +1,5 @@
export interface Color {
- R: string;
- G: string;
- B: string;
+ R: number;
+ G: number;
+ B: number;
}
\ No newline at end of file
diff --git a/src/types/headerletter.ts b/src/types/headerletter.ts
index 7546dff..a93756f 100644
--- a/src/types/headerletter.ts
+++ b/src/types/headerletter.ts
@@ -2,5 +2,5 @@ import { Color } from "./color.ts";
export interface HeaderLetter {
letter: string;
- color: Color;
+ color?: Color;
}
\ No newline at end of file
diff --git a/src/types/issue.ts b/src/types/issue.ts
index d8a544b..18dc0f0 100644
--- a/src/types/issue.ts
+++ b/src/types/issue.ts
@@ -1,35 +1,49 @@
-import { u32, u64 } from "@stellar/stellar-sdk/contract";
+// Base interface for common issue properties
+export interface BaseIssue {
+ id: number;
+ name: string;
+ cid: string;
+ creator: string;
+}
+
+// Main proposal issue interface extending base
+export interface ProposalIssue extends BaseIssue {
+ summary: string;
+ isCurrent: boolean;
+ previousCid?: string;
+ createdAt: Date;
+ updatedAt: Date;
+}
-export interface Issue {
- id: String;
- title: String,
- summary: String,
- paragraph_count: u32,
- positive_votes: u32,
- negative_votes: u32,
- telegram_handle: string,
- created_at: u64,
+// Interface for content holding
+interface ContentHolder {
+ content: string;
}
+// Type for issue content combining base fields and content
+export type ProposalContent = BaseIssue & ContentHolder;
+
export interface ListIssuesResponse {
- issues: Issue[];
+ proposals: ProposalIssue[]
}
-export interface GetParagraphsResponse {
- paragraphs: string[];
+export interface GetIssueResponse {
+ proposal: ProposalContent;
}
export interface AddIssueRequest {
- title: string;
- paragraphs: string[];
+ name: string;
+ description: string;
+ creator: string;
}
export interface AddIssueResponse {
- result: boolean;
+ cid: string;
}
export interface VoteIssueRequest {
issue_id: string;
+ wallet: string;
vote: 'Positive' | 'Negative';
}
@@ -39,10 +53,7 @@ export interface VoteIssueResponse {
positive: boolean;
}
-export interface GetVoteRequest {
- issue_id: number;
-}
-
-export interface GetVoteResponse {
- vote?: 'Positive' | 'Negative';
-}
+export interface IssueSelection {
+ proposal: ProposalContent;
+ cid: string;
+}
\ No newline at end of file
diff --git a/src/types/vote.ts b/src/types/vote.ts
new file mode 100644
index 0000000..3eaaf32
--- /dev/null
+++ b/src/types/vote.ts
@@ -0,0 +1,4 @@
+export enum VoteType {
+ Positive = 0,
+ Negative = 1,
+}
\ No newline at end of file
diff --git a/src/utils/amendment.ts b/src/utils/amendment.ts
new file mode 100644
index 0000000..d79e110
--- /dev/null
+++ b/src/utils/amendment.ts
@@ -0,0 +1,13 @@
+export const getChipClass = (status: string) => {
+ const normalizedStatus = status.toLowerCase();
+ switch (normalizedStatus) {
+ case 'approved':
+ return 'bg-green-500 text-white';
+ case 'rejected':
+ return 'bg-red-500 text-white';
+ case 'proposed':
+ return 'bg-transparent border border-black text-black';
+ default:
+ return '';
+ }
+}
\ No newline at end of file
diff --git a/src/utils/wallet.ts b/src/utils/wallet.ts
new file mode 100644
index 0000000..6f3fc51
--- /dev/null
+++ b/src/utils/wallet.ts
@@ -0,0 +1,3 @@
+export const truncateWallet = (wallet: string) => {
+ return `${wallet.slice(0, 6)}...${wallet.slice(-4)}`;
+}
\ No newline at end of file
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 10de317..ea7fdcf 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -1,130 +1,135 @@
-
-
-
-
-
- Add Issue
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
index 7da474f..554ba25 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -3,11 +3,18 @@ export default {
content: ['./index.html', './src/**/*.{vue,js,ts}'],
theme: {
extend: {
+ fontSize: {
+ 'mxs': '0.92rem',
+ },
height: {
'15/16': '93.75%',
'1/16': '6.25%',
'11/12': '91.666666%',
'1/10': '8.333333%'
+ },
+ lineHeight: {
+ 'short': '0.9',
+ 'tighter': '1.15',
}
},
fontFamily: {
diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo
new file mode 100644
index 0000000..dcabc7f
--- /dev/null
+++ b/tsconfig.app.tsbuildinfo
@@ -0,0 +1 @@
+{"root":["./src/main.ts","./src/vite-env.d.ts","./src/composables/usefetch.ts","./src/composables/usefilternotifier.ts","./src/composables/useloading.ts","./src/composables/usemodal.ts","./src/composables/usesession.ts","./src/router/index.ts","./src/stores/filterstore.ts","./src/types/amendment.ts","./src/types/color.ts","./src/types/comment.ts","./src/types/dropdown.ts","./src/types/headerletter.ts","./src/types/issue.ts","./src/types/user.ts","./src/types/vote.ts","./src/utils/amendment.ts","./src/utils/eventbus.ts","./src/utils/wallet.ts","./src/app.vue","./src/components/amendmentcarousel.vue","./src/components/brandbar.vue","./src/components/chip.vue","./src/components/coloredheader.vue","./src/components/dropdown.vue","./src/components/issuecontainer.vue","./src/components/loadingspinner.vue","./src/components/postbuilder.vue","./src/components/sidebar.vue","./src/components/thread.vue","./src/components/votingcontrols.vue","./src/components/icons/abstain.vue","./src/components/icons/arrow.vue","./src/components/icons/cancel.vue","./src/components/icons/confirm.vue","./src/components/icons/downarrow.vue","./src/components/icons/filter.vue","./src/components/icons/menu.vue","./src/components/icons/speechbubbles.vue","./src/components/icons/thumbsdown.vue","./src/components/icons/thumbsup.vue","./src/components/modals/amendmentmodal.vue","./src/components/modals/filtermodal.vue","./src/components/modals/issuemodal.vue","./src/components/modals/modal.vue","./src/components/modals/registrationmodal.vue","./src/views/homeview.vue"],"errors":true,"version":"5.9.2"}
\ No newline at end of file
diff --git a/tsconfig.node.tsbuildinfo b/tsconfig.node.tsbuildinfo
new file mode 100644
index 0000000..240b892
--- /dev/null
+++ b/tsconfig.node.tsbuildinfo
@@ -0,0 +1 @@
+{"root":["./vite.config.ts"],"version":"5.9.2"}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 4aaeaa3..9f5c757 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,14 +1,19 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import tailwindcss from '@tailwindcss/vite'
+import vueDevTools from 'vite-plugin-vue-devtools'
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [vue()],
+ plugins: [
+ vue(),
+ vueDevTools(),
+ tailwindcss()
+ ],
server: {
proxy: {
'/api': {
- target: 'http://127.0.0.1:8080',
- rewrite: (url: string) => url.replace(/\/api/, ''),
+ target: 'http://127.0.0.1:7300',
}
}
}