tests, add and view time
This commit is contained in:
parent
d3676e635f
commit
d767df3088
61
src/api.ts
61
src/api.ts
|
|
@ -128,8 +128,16 @@ export async function fetchIssues(
|
||||||
color: l.color,
|
color: l.color,
|
||||||
})),
|
})),
|
||||||
pull_request: item.pull_request,
|
pull_request: item.pull_request,
|
||||||
|
total_tracked_time: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Fetch tracked times concurrently for all issues
|
||||||
|
await Promise.all(
|
||||||
|
issues.map(async (issue) => {
|
||||||
|
issue.total_tracked_time = await fetchIssueTrackedTimeTotal(config, issue.number);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return { issues, totalCount };
|
return { issues, totalCount };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
|
|
@ -150,6 +158,7 @@ export async function fetchIssue(
|
||||||
try {
|
try {
|
||||||
const response = await client.get(`/repos/${config.owner}/${config.repo}/issues/${issueNumber}`);
|
const response = await client.get(`/repos/${config.owner}/${config.repo}/issues/${issueNumber}`);
|
||||||
const item = response.data;
|
const item = response.data;
|
||||||
|
const trackedTime = await fetchIssueTrackedTimeTotal(config, issueNumber);
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
number: item.number,
|
number: item.number,
|
||||||
|
|
@ -170,6 +179,7 @@ export async function fetchIssue(
|
||||||
color: l.color,
|
color: l.color,
|
||||||
})),
|
})),
|
||||||
pull_request: item.pull_request,
|
pull_request: item.pull_request,
|
||||||
|
total_tracked_time: trackedTime,
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
|
|
@ -424,3 +434,54 @@ export async function editIssue(
|
||||||
throw new Error(`Network Error: ${error.message}`);
|
throw new Error(`Network Error: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds time to an issue.
|
||||||
|
* @param time Time in seconds
|
||||||
|
*/
|
||||||
|
export async function addIssueTime(
|
||||||
|
config: Config,
|
||||||
|
issueNumber: number,
|
||||||
|
time: number
|
||||||
|
): Promise<any> {
|
||||||
|
const client = createAxiosInstance(config);
|
||||||
|
try {
|
||||||
|
const response = await client.post(`/repos/${config.owner}/${config.repo}/issues/${issueNumber}/times`, {
|
||||||
|
time,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response) {
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
throw new Error('Unauthorized: You must be logged in with a token to add time.');
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to add time: ${error.response.data?.message || error.message}`);
|
||||||
|
}
|
||||||
|
throw new Error(`Network Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the total tracked time for a specific issue.
|
||||||
|
* @returns Total time in seconds
|
||||||
|
*/
|
||||||
|
export async function fetchIssueTrackedTimeTotal(
|
||||||
|
config: Config,
|
||||||
|
issueNumber: number
|
||||||
|
): Promise<number> {
|
||||||
|
const client = createAxiosInstance(config);
|
||||||
|
try {
|
||||||
|
const response = await client.get(`/repos/${config.owner}/${config.repo}/issues/${issueNumber}/times`);
|
||||||
|
const times: any[] = response.data;
|
||||||
|
let total = 0;
|
||||||
|
for (const t of times) {
|
||||||
|
if (t && typeof t.time === 'number') {
|
||||||
|
total += t.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
} catch (error: any) {
|
||||||
|
// If times are not supported or missing, just return 0
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,9 @@ async function bootstrap() {
|
||||||
body: '',
|
body: '',
|
||||||
activeField: 'title',
|
activeField: 'title',
|
||||||
},
|
},
|
||||||
|
addTimeForm: {
|
||||||
|
timeInput: '',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// If parameters are provided, try direct connection first
|
// If parameters are provided, try direct connection first
|
||||||
|
|
|
||||||
181
src/tui.ts
181
src/tui.ts
|
|
@ -1,7 +1,7 @@
|
||||||
import readline from 'readline';
|
import readline from 'readline';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { AppState, Issue, Comment } from './types.js';
|
import { AppState, Issue, Comment } from './types.js';
|
||||||
import { fetchIssues, fetchIssue, fetchIssueComments, validateConnection, normalizeUrl, authenticateAndFetchRepos, createIssue, createIssueComment, editIssue } from './api.js';
|
import { fetchIssues, fetchIssue, fetchIssueComments, validateConnection, normalizeUrl, authenticateAndFetchRepos, createIssue, createIssueComment, editIssue, addIssueTime } from './api.js';
|
||||||
import { saveGlobalConfig } from './config.js';
|
import { saveGlobalConfig } from './config.js';
|
||||||
|
|
||||||
// Setup readline for stdin keypress events
|
// Setup readline for stdin keypress events
|
||||||
|
|
@ -73,12 +73,20 @@ export function formatDate(dateStr: string): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Utility to safely truncate strings
|
||||||
* Helper to truncate strings to a specific length.
|
|
||||||
*/
|
|
||||||
function truncate(str: string, length: number): string {
|
function truncate(str: string, length: number): string {
|
||||||
if (str.length <= length) return str;
|
if (str.length <= length) return str;
|
||||||
return str.substring(0, length - 3) + '...';
|
return str.substring(0, length - 1) + '…';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility to format seconds into h m format
|
||||||
|
function formatTime(seconds?: number): string {
|
||||||
|
if (!seconds || seconds <= 0) return '0m';
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
if (h > 0 && m > 0) return `${h}h ${m}m`;
|
||||||
|
if (h > 0) return `${h}h`;
|
||||||
|
return `${m}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -281,6 +289,8 @@ export class TuiEngine {
|
||||||
this.handleAddCommentKeypress(str, key);
|
this.handleAddCommentKeypress(str, key);
|
||||||
} else if (this.state.screen === 'edit-issue') {
|
} else if (this.state.screen === 'edit-issue') {
|
||||||
this.handleEditIssueKeypress(str, key);
|
this.handleEditIssueKeypress(str, key);
|
||||||
|
} else if (this.state.screen === 'add-time') {
|
||||||
|
this.handleAddTimeKeypress(str, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -727,6 +737,16 @@ export class TuiEngine {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str === 't' || str === 'T') {
|
||||||
|
if (this.state.selectedIssue) {
|
||||||
|
this.state.screen = 'add-time';
|
||||||
|
this.state.addTimeForm.timeInput = '';
|
||||||
|
this.state.error = null;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((key && (key.name === 'escape' || key.name === 'backspace')) || str === '\u001b' || str === 'q' || str === 'Q') {
|
if ((key && (key.name === 'escape' || key.name === 'backspace')) || str === '\u001b' || str === 'q' || str === 'Q') {
|
||||||
this.state.screen = 'list';
|
this.state.screen = 'list';
|
||||||
this.state.selectedIssue = null;
|
this.state.selectedIssue = null;
|
||||||
|
|
@ -759,6 +779,8 @@ export class TuiEngine {
|
||||||
this.renderAddCommentScreen(cols, rows);
|
this.renderAddCommentScreen(cols, rows);
|
||||||
} else if (this.state.screen === 'edit-issue') {
|
} else if (this.state.screen === 'edit-issue') {
|
||||||
this.renderEditIssueScreen(cols, rows);
|
this.renderEditIssueScreen(cols, rows);
|
||||||
|
} else if (this.state.screen === 'add-time') {
|
||||||
|
this.renderAddTimeScreen(cols, rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -943,8 +965,9 @@ export class TuiEngine {
|
||||||
const authorWidth = 14;
|
const authorWidth = 14;
|
||||||
const createdWidth = 12;
|
const createdWidth = 12;
|
||||||
const commentsWidth = 6;
|
const commentsWidth = 6;
|
||||||
|
const timeWidth = 7;
|
||||||
// Title takes all remaining space
|
// Title takes all remaining space
|
||||||
const titleWidth = Math.max(20, cols - idWidth - typeWidth - stateWidth - authorWidth - createdWidth - commentsWidth - 8);
|
const titleWidth = Math.max(20, cols - idWidth - typeWidth - stateWidth - authorWidth - createdWidth - commentsWidth - timeWidth - 9);
|
||||||
|
|
||||||
// Render Table Header
|
// Render Table Header
|
||||||
const padHeader = (title: string, w: number) => chalk.bold.white(title.padEnd(w));
|
const padHeader = (title: string, w: number) => chalk.bold.white(title.padEnd(w));
|
||||||
|
|
@ -958,7 +981,8 @@ export class TuiEngine {
|
||||||
padHeader('Title', titleWidth) + borderCh + ' ' +
|
padHeader('Title', titleWidth) + borderCh + ' ' +
|
||||||
padHeader('Author', authorWidth) + borderCh + ' ' +
|
padHeader('Author', authorWidth) + borderCh + ' ' +
|
||||||
padHeader('Created', createdWidth) + borderCh + ' ' +
|
padHeader('Created', createdWidth) + borderCh + ' ' +
|
||||||
padHeader('Coms', commentsWidth) + borderCh
|
padHeader('Coms', commentsWidth) + borderCh + ' ' +
|
||||||
|
padHeader('Time', timeWidth) + borderCh
|
||||||
);
|
);
|
||||||
console.log(chalk.bold.hex('#4A90E2')('├' + '─'.repeat(cols - 2) + '┤'));
|
console.log(chalk.bold.hex('#4A90E2')('├' + '─'.repeat(cols - 2) + '┤'));
|
||||||
|
|
||||||
|
|
@ -995,6 +1019,7 @@ export class TuiEngine {
|
||||||
const authorStr = truncate(issue.user.login, authorWidth).padEnd(authorWidth);
|
const authorStr = truncate(issue.user.login, authorWidth).padEnd(authorWidth);
|
||||||
const createdStr = formatDate(issue.created_at).substring(0, 10).padEnd(createdWidth);
|
const createdStr = formatDate(issue.created_at).substring(0, 10).padEnd(createdWidth);
|
||||||
const commentsStr = String(issue.comments).padEnd(commentsWidth);
|
const commentsStr = String(issue.comments).padEnd(commentsWidth);
|
||||||
|
const timeStr = formatTime(issue.total_tracked_time).padEnd(timeWidth);
|
||||||
|
|
||||||
let rowText =
|
let rowText =
|
||||||
' ' + idStr + borderCh +
|
' ' + idStr + borderCh +
|
||||||
|
|
@ -1003,7 +1028,8 @@ export class TuiEngine {
|
||||||
' ' + (issue.pull_request ? chalk.magenta(titleStr) : titleStr) + borderCh +
|
' ' + (issue.pull_request ? chalk.magenta(titleStr) : titleStr) + borderCh +
|
||||||
' ' + authorStr + borderCh +
|
' ' + authorStr + borderCh +
|
||||||
' ' + createdStr + borderCh +
|
' ' + createdStr + borderCh +
|
||||||
' ' + commentsStr + borderCh;
|
' ' + commentsStr + borderCh +
|
||||||
|
' ' + timeStr + borderCh;
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
rowText = chalk.bgHex('#2E4E7E').white.bold(rowText);
|
rowText = chalk.bgHex('#2E4E7E').white.bold(rowText);
|
||||||
|
|
@ -1092,8 +1118,9 @@ export class TuiEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
const labels = issue.labels.map(l => chalk.bgHex('#' + l.color).black(` ${l.name} `)).join(' ');
|
const labels = issue.labels.map(l => chalk.bgHex('#' + l.color).black(` ${l.name} `)).join(' ');
|
||||||
|
const timeStr = formatTime(issue.total_tracked_time);
|
||||||
|
|
||||||
console.log(borderCh + ` State: ${stateLabel} Author: ${chalk.cyan(issue.user.login)} Created: ${formatDate(issue.created_at)} Updated: ${formatDate(issue.updated_at)}`.padEnd(cols - 2) + borderCh);
|
console.log(borderCh + ` State: ${stateLabel} Author: ${chalk.cyan(issue.user.login)} Created: ${formatDate(issue.created_at)} Updated: ${formatDate(issue.updated_at)} Time: ${chalk.yellow(timeStr)}`.padEnd(cols - 2 + (issue.state === 'open' ? 10 : 9) + 10 + 10) + borderCh);
|
||||||
if (labels) {
|
if (labels) {
|
||||||
console.log(borderCh + ` Labels: ${labels}`.padEnd(cols - 2 + labels.length - issue.labels.map(l=>l.name.length + 10).reduce((a,b)=>a+b, 0)) + borderCh); // offset for raw ANSI chars
|
console.log(borderCh + ` Labels: ${labels}`.padEnd(cols - 2 + labels.length - issue.labels.map(l=>l.name.length + 10).reduce((a,b)=>a+b, 0)) + borderCh); // offset for raw ANSI chars
|
||||||
}
|
}
|
||||||
|
|
@ -1160,7 +1187,7 @@ export class TuiEngine {
|
||||||
scrollHelp += chalk.yellow(` (${pct}%)`);
|
scrollHelp += chalk.yellow(` (${pct}%)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const helpLine = chalk.gray(` [Esc/Backspace] Back to List [C] Add Comment [E] Edit [R] Reload [O] Settings ${scrollHelp}`);
|
const helpLine = chalk.gray(` [Esc/Backspace] Back to List [C] Add Comment [E] Edit [T] Add Time [R] Reload [O] Settings ${scrollHelp}`);
|
||||||
process.stdout.write(helpLine);
|
process.stdout.write(helpLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1708,5 +1735,139 @@ export class TuiEngine {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderAddTimeScreen(cols: number, rows: number) {
|
||||||
|
const borderCh = chalk.bold.hex('#4A90E2')('│');
|
||||||
|
|
||||||
|
// Header
|
||||||
|
const title = ' Add Time ';
|
||||||
|
const leftLen = stripAnsi(title).length;
|
||||||
|
let leftHeader = chalk.bold.hex('#4A90E2')(title);
|
||||||
|
|
||||||
|
console.log(leftHeader + ' '.repeat(Math.max(1, cols - leftLen - 1)));
|
||||||
|
console.log(chalk.bold.hex('#4A90E2')('┌' + '─'.repeat(cols - 2) + '┐'));
|
||||||
|
|
||||||
|
if (this.state.error) {
|
||||||
|
console.log(borderCh + chalk.red(` Error: ${this.state.error}`).padEnd(cols - 2) + borderCh);
|
||||||
|
console.log(chalk.bold.hex('#4A90E2')('├' + '─'.repeat(cols - 2) + '┤'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = this.state.addTimeForm;
|
||||||
|
|
||||||
|
// Time Field
|
||||||
|
const timeLabel = chalk.yellow.bold(' Time Spent (e.g. 1h 30m, 45m): ');
|
||||||
|
console.log(borderCh + timeLabel + ' '.repeat(Math.max(0, cols - stripAnsi(timeLabel).length - 2)) + borderCh);
|
||||||
|
console.log(chalk.bold.hex('#4A90E2')('├' + '─'.repeat(cols - 2) + '┤'));
|
||||||
|
|
||||||
|
// Input Box
|
||||||
|
let visLine = form.timeInput;
|
||||||
|
if (visLine.length > cols - 5) {
|
||||||
|
visLine = visLine.substring(visLine.length - (cols - 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
visLine = visLine + chalk.inverse(' ') + ' '.repeat(Math.max(0, cols - 5 - stripAnsi(visLine).length));
|
||||||
|
|
||||||
|
console.log(borderCh + ' ' + visLine + ' ' + borderCh);
|
||||||
|
|
||||||
|
console.log(chalk.bold.hex('#4A90E2')('└' + '─'.repeat(cols - 2) + '┘'));
|
||||||
|
|
||||||
|
// Help line
|
||||||
|
const helpLine = chalk.gray(' [Ctrl+S] Submit [Esc] Cancel');
|
||||||
|
const issueStr = this.state.selectedIssue ? chalk.bold.cyan(` Issue #${this.state.selectedIssue.number} `) : '';
|
||||||
|
const issueLen = stripAnsi(issueStr).length;
|
||||||
|
|
||||||
|
const maxHelpLen = cols - issueLen - 1;
|
||||||
|
let helpLinePlain = helpLine;
|
||||||
|
if (stripAnsi(helpLinePlain).length > maxHelpLen) {
|
||||||
|
helpLinePlain = chalk.gray(stripAnsi(helpLinePlain).substring(0, maxHelpLen - 3) + '...');
|
||||||
|
}
|
||||||
|
|
||||||
|
const spaces = cols - stripAnsi(helpLinePlain).length - issueLen - 1;
|
||||||
|
process.stdout.write(helpLinePlain + ' '.repeat(Math.max(0, spaces)) + issueStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleAddTimeKeypress(str: string, key: any) {
|
||||||
|
if (key && key.name === 'escape') {
|
||||||
|
this.state.screen = 'details';
|
||||||
|
this.state.error = null;
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key && key.ctrl && key.name === 's') {
|
||||||
|
const input = this.state.addTimeForm.timeInput.trim();
|
||||||
|
if (!input) {
|
||||||
|
this.state.error = 'Time input cannot be empty.';
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.state.selectedIssue) {
|
||||||
|
this.state.error = 'No issue selected.';
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse time input (e.g. 1h 30m, 45m, 2h)
|
||||||
|
let totalSeconds = 0;
|
||||||
|
const hMatch = input.match(/(\d+)\s*h/);
|
||||||
|
const mMatch = input.match(/(\d+)\s*m/);
|
||||||
|
|
||||||
|
if (hMatch) {
|
||||||
|
totalSeconds += parseInt(hMatch[1], 10) * 3600;
|
||||||
|
}
|
||||||
|
if (mMatch) {
|
||||||
|
totalSeconds += parseInt(mMatch[1], 10) * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither h nor m matched, but it's just a number, assume minutes
|
||||||
|
if (!hMatch && !mMatch && /^\d+$/.test(input)) {
|
||||||
|
totalSeconds += parseInt(input, 10) * 60;
|
||||||
|
} else if (!hMatch && !mMatch) {
|
||||||
|
this.state.error = "Invalid format. Use '1h 30m', '45m', or just minutes like '45'.";
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalSeconds <= 0) {
|
||||||
|
this.state.error = "Time must be greater than 0.";
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.error = null;
|
||||||
|
this.state.loading = true;
|
||||||
|
this.render();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await addIssueTime(this.state.config, this.state.selectedIssue.number, totalSeconds);
|
||||||
|
this.state.screen = 'details';
|
||||||
|
this.state.loading = false;
|
||||||
|
// Reload to show the time tracked
|
||||||
|
this.reloadSingleIssue();
|
||||||
|
} catch (err: any) {
|
||||||
|
this.state.error = err.message;
|
||||||
|
this.state.loading = false;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = this.state.addTimeForm.timeInput;
|
||||||
|
|
||||||
|
if (key && key.name === 'backspace') {
|
||||||
|
if (val.length > 0) {
|
||||||
|
this.state.addTimeForm.timeInput = val.slice(0, -1);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str && str.length === 1 && !key.ctrl && !key.meta && str !== '\n' && str !== '\r') {
|
||||||
|
this.state.error = null;
|
||||||
|
this.state.addTimeForm.timeInput += str;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
src/types.ts
10
src/types.ts
|
|
@ -30,6 +30,7 @@ export interface Issue {
|
||||||
comments: number;
|
comments: number;
|
||||||
labels: Label[];
|
labels: Label[];
|
||||||
pull_request?: any | null;
|
pull_request?: any | null;
|
||||||
|
total_tracked_time?: number; // Total time in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Comment {
|
export interface Comment {
|
||||||
|
|
@ -64,7 +65,11 @@ export interface EditIssueForm {
|
||||||
activeField: 'title' | 'body';
|
activeField: 'title' | 'body';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ScreenType = 'setup' | 'repo-picker' | 'list' | 'details' | 'create-issue' | 'add-comment' | 'edit-issue';
|
export interface AddTimeForm {
|
||||||
|
timeInput: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScreenType = 'setup' | 'repo-picker' | 'list' | 'details' | 'create-issue' | 'add-comment' | 'edit-issue' | 'add-time';
|
||||||
|
|
||||||
|
|
||||||
export interface RepoItem {
|
export interface RepoItem {
|
||||||
|
|
@ -116,5 +121,8 @@ export interface AppState {
|
||||||
|
|
||||||
// Edit Issue Form State
|
// Edit Issue Form State
|
||||||
editIssueForm: EditIssueForm;
|
editIssueForm: EditIssueForm;
|
||||||
|
|
||||||
|
// Add Time Form State
|
||||||
|
addTimeForm: AddTimeForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const configPath = os.homedir() + '/.config/fjtui/fjtui.json';
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
|
||||||
|
axios.post(config.url + '/api/v1/repos/' + config.repo + '/issues/1/times', { time: 3600 }, {
|
||||||
|
headers: { 'Authorization': 'token ' + config.token }
|
||||||
|
}).then(r => console.log('Added time')).catch(e => console.error(e.message));
|
||||||
|
|
||||||
|
axios.get(config.url + '/api/v1/repos/' + config.repo + '/issues/1', {
|
||||||
|
headers: { 'Authorization': 'token ' + config.token }
|
||||||
|
}).then(r => console.log(Object.keys(r.data))).catch(e => console.error(e.message));
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const configPath = os.homedir() + '/.config/fjtui/fjtui.json';
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
|
||||||
|
axios.get(config.url + '/api/v1/repos/' + config.repo + '/issues/1/times', {
|
||||||
|
headers: { 'Authorization': 'token ' + config.token }
|
||||||
|
}).then(r => console.log(JSON.stringify(r.data, null, 2))).catch(e => console.error(e.message));
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const configPath = os.homedir() + '/.config/fjtui/fjtui.json';
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
|
||||||
|
axios.get(config.url + '/api/v1/repos/' + config.repo + '/times', {
|
||||||
|
headers: { 'Authorization': 'token ' + config.token }
|
||||||
|
}).then(r => console.log(JSON.stringify(r.data.slice(0, 2), null, 2))).catch(e => console.error(e.message));
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const configPath = os.homedir() + '/.config/fjtui/fjtui.json';
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
|
||||||
|
axios.get(config.url + '/api/v1/repos/' + config.repo + '/issues', {
|
||||||
|
headers: { 'Authorization': 'token ' + config.token }
|
||||||
|
}).then(r => console.log(JSON.stringify(r.data[0], null, 2))).catch(e => console.error(e.message));
|
||||||
Loading…
Reference in New Issue