Compare commits

..

17 Commits

Author SHA1 Message Date
59dcb4c18a Merge branch 'develop' into dist/ubuntu/bionic/develop 2020-04-30 15:46:44 +02:00
61ef77cf16 Merge branch 'develop' into dist/ubuntu/bionic/develop 2020-01-13 14:18:30 +01:00
acde7436da Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-13 15:50:00 +01:00
b3d458ebac Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-13 14:21:32 +01:00
5284633e7f Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-13 13:32:10 +01:00
5c77f22c26 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-06 17:15:38 +01:00
204d6869ec Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-06 10:02:46 +01:00
35683a4584 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 22:46:30 +01:00
64dd215038 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 22:37:35 +01:00
d8192e6953 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 17:09:25 +01:00
4d3175a25e Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 16:15:49 +01:00
ca7b8aac47 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 14:44:48 +01:00
971bf25f16 Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 13:57:27 +01:00
a2ab012c5f Create /var/lib/gitea-kan dir 2019-12-05 13:20:19 +01:00
fb815c4d83 Define gitea-kan configuration env vars 2019-12-05 13:19:54 +01:00
88a0a5bebe Merge branch 'develop' into dist/ubuntu/bionic/develop 2019-12-05 13:12:10 +01:00
f54b579ae3 Add Debian packaging recipe 2019-12-05 13:10:02 +01:00
30 changed files with 322 additions and 10372 deletions

View File

@ -9,7 +9,6 @@
### Procédure ### Procédure
```bash ```bash
cd client && npm install # Installation des dépendances client
make watch # Surveiller les modifications sur le sources et compiler/démarrer le serveur make watch # Surveiller les modifications sur le sources et compiler/démarrer le serveur
``` ```

9933
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,9 @@
"@fortawesome/fontawesome-free": "^5.11.2", "@fortawesome/fontawesome-free": "^5.11.2",
"@types/node": "^13.13.4", "@types/node": "^13.13.4",
"@types/react-dom": "^16.9.7", "@types/react-dom": "^16.9.7",
"@types/react-redux": "^7.1.7",
"@types/react-router-dom": "^5.1.5", "@types/react-router-dom": "^5.1.5",
"@types/uuid": "^7.0.3", "@types/uuid": "^7.0.3",
"@types/react-redux": "^7.1.7",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"css-loader": "^1.0.1", "css-loader": "^1.0.1",
"extract-loader": "^3.1.0", "extract-loader": "^3.1.0",
@ -42,15 +42,14 @@
"resolve-url-loader": "^3.0.0", "resolve-url-loader": "^3.0.0",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"ts-loader": "^7.0.2",
"webpack": "^4.25.0", "webpack": "^4.25.0",
"webpack-cleanup-plugin": "^0.5.1", "webpack-cleanup-plugin": "^0.5.1",
"webpack-cli": "^3.1.2" "webpack-cli": "^3.1.2",
"ts-loader": "^7.0.2"
}, },
"dependencies": { "dependencies": {
"@lourenci/react-kanban": "^2.0.0", "@lourenci/react-kanban": "^0.15.0",
"bulma": "^0.7.2", "bulma": "^0.7.2",
"bulma-switch": "^2.0.0",
"react": "^16.12.0", "react": "^16.12.0",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-redux": "^7.1.3", "react-redux": "^7.1.3",

View File

@ -1,21 +1,18 @@
import React, { Fragment } from 'react'; import React from 'react';
import { Page } from '../Page'; import { Page } from '../Page';
import { connect, DispatchProp } from 'react-redux'; import { connect, DispatchProp } from 'react-redux';
import Board, { addColumn } from '@lourenci/react-kanban'; import Board from '@lourenci/react-kanban';
import { fetchBoards } from '../../store/actions/boards'; import { fetchBoards } from '../../store/actions/boards';
import { createIssue, fetchIssues } from '../../store/actions/issues'; import { createIssue } from '../../store/actions/issues';
import { buildKanboard, moveCard } from '../../store/actions/kanboards'; import { buildKanboard, moveCard } from '../../store/actions/kanboards';
import { Loader } from '../Loader'; import { Loader } from '../Loader';
import { IssueCard } from './IssueCard'; import { IssueCard } from './IssueCard';
import { Modal } from '../Modal'; import { Modal } from '../Modal';
import { fetchProjectMilestones } from '../../store/actions/projects';
export interface BoardPageProps extends DispatchProp { export interface BoardPageProps extends DispatchProp {
board: any board: any
kanboard: any kanboard: any
milestones: any
} }
export class BoardPage extends React.Component<BoardPageProps> { export class BoardPage extends React.Component<BoardPageProps> {
state = { state = {
@ -25,10 +22,7 @@ export class BoardPage extends React.Component<BoardPageProps> {
title: "", title: "",
body: "", body: "",
project: "" project: ""
}, }
compactMode: true,
hasError: false,
selectedMilestone: "",
} }
onNewCardTitleChange: (evt: any) => void; onNewCardTitleChange: (evt: any) => void;
@ -46,104 +40,41 @@ export class BoardPage extends React.Component<BoardPageProps> {
this.onNewCardSaveClick = this.onNewCardSaveClick.bind(this); this.onNewCardSaveClick = this.onNewCardSaveClick.bind(this);
} }
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error(error, errorInfo);
}
handleMilestonesChange(e: any) {
console.log("EVT VALUE", e.target.value);
let m = (e.target as HTMLInputElement).value;
this.setState(state => ({ ...state, selectedMilestone: m }), this.requestBuildKanboard);
//this.requestBuildKanboard();
}
render() { render() {
const { board } = this.props; const { board } = this.props;
return ( return (
<Page title={`${board ? (board.title + ' - ') : ''}GenGitKan`}> <Page title={`${board ? (board.title + ' - ') : ''}GenGitKan`}>
{this.renderBoard()} <div className="container is-fluid">
{this.renderBoard()}
</div>
</Page> </Page>
); );
} }
renderBoard() { renderBoard() {
const { kanboard, board, milestones } = this.props; const { kanboard } = this.props;
if (!kanboard) { if (!kanboard) {
return <Loader></Loader> return <Loader></Loader>
} }
return ( return (
<Fragment> <div className="kanboard-container is-fullheight">
<nav className="navbar is-light"> <Board disableLaneDrag={true}
<div className="container is-fluid"> renderCard={this.renderCard}
<div className="navbar-start"> renderLaneHeader={this.renderLaneHeader}
<div className="navbar-item"> onCardDragEnd={this.onCardDragEnd.bind(this)}>
{board.title} {kanboard}
</div> </Board>
</div> { this.renderNewCardModal() }
<div className="navbar-end"> </div>
<div className="navbar-item">
<div className="select">
<select onChange={this.handleMilestonesChange.bind(this)}>
<option value="">Sélectionner un jalon</option>
{
milestones.length > 0 ? milestones.map((k: any) => {
return (
<option value={k.title} key={k.id}>{k.title}</option>
)
}) : ""
}
</select>
</div>
</div>
<div className="navbar-item">
<div className="field">
<input id="compactMode"
checked={this.state.compactMode}
onChange={this.onCompactModeChange.bind(this)}
type="checkbox"
className="switch is-outlined is-success"
name="compactMode"
/>
<label htmlFor="compactMode">Mode compact</label>
</div>
</div>
<a href={`#/boards/${board.id}/edit`} className="navbar-item">
<span className="icon">
<i className="fa fa-edit fa-fw"></i>
</span>
<span>Modifier le tableau</span>
</a>
</div>
</div>
</nav>
<div className="container is-fluid">
<div className="kanboard-container is-fullheight">
<Board
renderCard={this.renderCard.bind(this)}
renderColumnHeader={this.renderLaneHeader.bind(this)}
onCardDragEnd={this.onCardDragEnd.bind(this)}
disableColumnDrag={true}
>
{kanboard}
</Board>
{this.renderNewCardModal()}
</div>
</div>
</Fragment>
); );
} }
renderNewCardModal() { renderNewCardModal() {
const { newCardModalActive, newCardLaneID } = this.state; const { newCardModalActive, newCardLaneID } = this.state;
const { board } = this.props; const { board } = this.props;
if (!board || newCardLaneID === undefined) return null; if (!board || !newCardLaneID) return null;
return ( return (
<Modal active={newCardModalActive}> <Modal active={newCardModalActive}>
<div className="new-card-modal"> <div className="new-card-modal">
@ -155,17 +86,17 @@ export class BoardPage extends React.Component<BoardPageProps> {
<div className="message-body"> <div className="message-body">
<div className="field"> <div className="field">
<div className="control"> <div className="control">
<input className="input is-medium" type="text" <input className="input is-medium" type="text"
placeholder="Titre de votre ticket..." placeholder="Titre de votre ticket..."
value={this.state.newCard.title} value={this.state.newCard.title}
onChange={this.onNewCardTitleChange} /> onChange={this.onNewCardTitleChange} />
</div> </div>
</div> </div>
<div className="field"> <div className="field">
<div className="control"> <div className="control">
<textarea className="textarea" <textarea className="textarea"
placeholder="Description du nouveau ticket..." placeholder="Description du nouveau ticket..."
value={this.state.newCard.body} value={this.state.newCard.body}
onChange={this.onNewCardBodyChange} onChange={this.onNewCardBodyChange}
rows={10}> rows={10}>
</textarea> </textarea>
@ -175,7 +106,7 @@ export class BoardPage extends React.Component<BoardPageProps> {
<div className="control is-expanded"> <div className="control is-expanded">
<div className="select is-fullwidth"> <div className="select is-fullwidth">
<select <select
value={this.state.newCard.project} value={this.state.newCard.project}
onChange={this.onNewCardProjectChange}> onChange={this.onNewCardProjectChange}>
{ {
board.projects.map((p: any, i: number) => { board.projects.map((p: any, i: number) => {
@ -188,7 +119,7 @@ export class BoardPage extends React.Component<BoardPageProps> {
</div> </div>
<div className="field is-grouped is-grouped-right"> <div className="field is-grouped is-grouped-right">
<p className="control"> <p className="control">
<a className="button is-light" <a className="button is-light"
onClick={this.onNewCardCloseClick}> onClick={this.onNewCardCloseClick}>
Annuler Annuler
</a> </a>
@ -208,7 +139,7 @@ export class BoardPage extends React.Component<BoardPageProps> {
} }
renderCard(card: any) { renderCard(card: any) {
return <IssueCard compact={this.state.compactMode} card={card} />; return <IssueCard card={card} />;
} }
renderLaneHeader(lane: any) { renderLaneHeader(lane: any) {
@ -216,49 +147,28 @@ export class BoardPage extends React.Component<BoardPageProps> {
<div className="kanboard-lane"> <div className="kanboard-lane">
<div className="level"> <div className="level">
<div className="level-left"> <div className="level-left">
<div className="level-item"> <h3 className="level-item is-size-3">{lane.title}</h3>
<span className="tag is-primary is-light is-normal">{lane.cards.length}</span>
</div>
<button className="button is-light level-item is-small expand"
onClick={this.onMinimizeColumn}>
<span className="icon">
<i className="fas fa-chevron-right" aria-hidden="true"></i>
</span>
</button>
<h3 className="level-item is-size-5">
{lane.title}
</h3>
</div> </div>
<div className="level-right is-show-expand"> <div className="level-right">
<button className="button is-light level-item is-small" <button className="button is-light level-item is-medium"
onClick={this.onNewCardClick.bind(this, lane.id)}> onClick={this.onNewCardClick.bind(this, lane.id)}>
<span className="icon"> <span className="icon">
<i className="fas fa-plus" aria-hidden="true"></i> <i className="fas fa-plus" aria-hidden="true"></i>
</span> </span>
</button> </button>
<button className="button is-light level-item is-small"
onClick={this.onMinimizeColumn}>
<span className="icon">
<i className="fas fa-chevron-left" aria-hidden="true"></i>
</span>
</button>
</div> </div>
</div> </div>
</div> </div>
) )
} }
onMinimizeColumn(e: any) { onCardDragEnd(source: any, dest: any) {
e.currentTarget.closest('.react-kanban-column').classList.toggle('minimized');
}
onCardDragEnd(card: any, source: any, dest: any) {
const { board } = this.props; const { board } = this.props;
this.props.dispatch(moveCard( this.props.dispatch(moveCard(
board.id, board.id,
source.fromColumnId, source.fromLaneId,
source.fromPosition, source.fromPosition,
dest.toColumnId, dest.toLaneId,
dest.toPosition dest.toPosition
)); ));
} }
@ -275,8 +185,8 @@ export class BoardPage extends React.Component<BoardPageProps> {
onNewCardClick(laneID: string) { onNewCardClick(laneID: string) {
const { board } = this.props; const { board } = this.props;
this.setState({ this.setState({
newCardModalActive: true, newCardModalActive: true,
newCardLaneID: laneID, newCardLaneID: laneID,
newCard: { newCard: {
title: "", title: "",
@ -308,9 +218,9 @@ export class BoardPage extends React.Component<BoardPageProps> {
const { board } = this.props; const { board } = this.props;
this.setState({ newCardModalActive: false }); this.setState({ newCardModalActive: false });
this.props.dispatch(createIssue( this.props.dispatch(createIssue(
newCard.project, newCard.project,
newCard.title, newCard.title,
newCard.body, newCard.body,
board.lanes[newCardLaneID].issueLabel board.lanes[newCardLaneID].issueLabel
)); ));
} }
@ -325,25 +235,16 @@ export class BoardPage extends React.Component<BoardPageProps> {
requestBuildKanboard() { requestBuildKanboard() {
const { board } = this.props; const { board } = this.props;
const { selectedMilestone } = this.state;
console.log("SELECTED MILESTONE", selectedMilestone)
if (!board) return; if (!board) return;
this.props.dispatch(fetchProjectMilestones(board.projects[0])); this.props.dispatch(buildKanboard(board));
this.props.dispatch(buildKanboard(board, selectedMilestone));
}
onCompactModeChange(evt: React.ChangeEvent) {
const checked = (evt.currentTarget as HTMLInputElement).checked;
this.setState(state => ({ ...state, compactMode: checked }));
} }
} }
export const ConnectedBoardPage = connect(function (state: any, props: any) { export const ConnectedBoardPage = connect(function(state: any, props: any) {
const boardID = props.match.params.id; const boardID = props.match.params.id;
return { return {
board: state.boards.byID[boardID], board: state.boards.byID[boardID],
kanboard: state.kanboards.byID[boardID], kanboard: state.kanboards.byID[boardID]
milestones: state.projects.milestones
}; };
})(BoardPage); })(BoardPage);

View File

@ -1,63 +1,50 @@
import React from 'react'; import React from 'react';
import { KanboardCard } from '../../types/kanboard';
export interface IssueCardProps { export interface IssueCardProps {
card: KanboardCard card: any
compact: boolean
} }
export class IssueCard extends React.PureComponent<IssueCardProps> { export class IssueCard extends React.PureComponent<IssueCardProps> {
render() { render() {
const { card } = this.props;
const { card, compact } = this.props;
const issueURLInfo = extractInfoFromIssueURL(card.issue.url); const issueURLInfo = extractInfoFromIssueURL(card.issue.url);
const projectURL = `${issueURLInfo.baseURL}/${issueURLInfo.owner}/${issueURLInfo.projectName}`; const projectURL = `${issueURLInfo.baseURL}/${issueURLInfo.owner}/${issueURLInfo.projectName}`;
const issueURL = `${projectURL}/issues/${card.issue.number}`; const issueURL = `${projectURL}/issues/${card.issue.number}`;
return ( return (
<div className="kanboard-card"> <div className="kanboard-card">
<div className="box has-padding-small is-radiusless"> <div className="box">
<div className="media"> <div className="media">
{
card.issue.assignee ?
<div className="media-left">
<figure className="image is-64x64">
<img src={card.issue.assignee.avatar_url} alt="Image" />
</figure>
<small>{`@${card.issue.assignee.login}`}</small>
</div>
: null
}
<div className="media-content"> <div className="media-content">
<div className="content"> <div className="content">
{ !compact && <p>
<nav className="level"> <strong>{`#${card.issue.number}`}</strong>&nbsp;
<div className="level-left"> { card.issue.milestone ? <small>{`- ${card.issue.milestone.title}`}</small> : null }
<div className="level-item"> <br />
<a target="_blank" href={issueURL}><strong>{`#${card.issue.number}`}</strong></a> <span className="is-size-6">{card.issue.title}</span>
</div> </p>
{ !compact &&
<div className="level-item">
<a target="_blank" href={projectURL}>{card.project}</a>
</div>
}
</div>
<div className="level-right">
{
card.issue.assignee && !compact ?
<div className="level-item">
<small>{`@${card.issue.assignee.login}`}</small>
</div>
: null
}
</div>
</nav>
}
{ compact &&
<a target="_blank" className="mr-1" href={issueURL}><strong>{`#${card.issue.number}`}</strong></a>
}
<span>{card.issue.title ? card.issue.title : ''}</span>
</div> </div>
{ !compact && </div>
<nav className="level"> </div>
<div className="level-left"></div> <div className="level is-mobile" style={{marginTop:'1rem'}}>
<div className="level-right"> <div className="level-left">
<div className="level-item is-size-7"> <small className="level-item"><a href={projectURL}>{card.project}</a></small>
{card.issue.milestone ? card.issue.milestone.title : ''} </div>
</div> <div className="level-right">
</div> <a className="level-item" target="_blank" href={issueURL}>
</nav> <span className="icon is-small has-text-info">
} <i className="fas fa-search" aria-hidden="true"></i>
</span>
</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,4 @@
@import 'bulma/bulma.sass'; @import 'bulma/bulma.sass';
@import '../../node_modules/@lourenci/react-kanban/dist/styles.css';
@import 'bulma-switch/dist/css/bulma-switch.sass';
@import '_base.scss'; @import '_base.scss';
@import '_loader.scss'; @import '_loader.scss';
@import '_kanboard.scss'; @import '_kanboard.scss';

View File

@ -11,15 +11,7 @@ html, body {
margin-top: $size-normal; margin-top: $size-normal;
} }
.has-padding-small {
padding: 1rem;
}
#app { #app {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
}
.mr-1 {
margin-right: 5px;
} }

View File

@ -44,12 +44,6 @@
.kanboard-lane { .kanboard-lane {
margin-bottom: $size-small; margin-bottom: $size-small;
background: $white;
top: 0;
.expand {
display: none;
}
} }
} }
@ -57,142 +51,4 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 50% !important; width: 50% !important;
}
.react-kanban-board {
max-height: calc(100vh - calc(52px * 2));
overflow: hidden;
overflow-x: scroll;
scrollbar-color: $grey-lighter, #f1f1f1;
scrollbar-width: 5px;
&::-webkit-scrollbar {
width: 5px;
height: 5px;
}
/* Track */
&::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: $grey-lighter;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
background: $green;
}
}
.react-kanban-column {
transition: width ease .2s;
max-width: 305px;
min-width: 305px;
position: relative;
max-height: 100%;
overflow-x: hidden;
overflow-y: scroll;
scrollbar-color: $grey-lighter, #f1f1f1;
scrollbar-width: 5px;
.kanboard-card {
display: block;
}
&.minimized {
max-width: 70px;
min-width: 70px;
writing-mode: vertical-rl;
text-orientation: sideways-right;
.level-item {
margin-right: 0;
}
.level-left {
margin-right: 0;
}
.level-right.is-show-expand {
display: none;
}
.kanboard-lane {
/*margin-right: -1em;*/
h3 {
margin-top: .5em;
/*margin-right: -.5em;*/
}
.tag {
writing-mode: horizontal-tb;
}
.expand {
display: block !important;
margin-top: .5em;
}
}
.kanboard-card {
display: none;
}
}
&::-webkit-scrollbar {
width: 5px;
}
/* Track */
&::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: $grey-lighter;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
background: $green;
}
}
.react-kanban-card__title {
position: sticky;
position: -webkit-sticky;
}
.react-kanban-column {
background: white !important;
}
.kanboard-card {
overflow-wrap: break-word;
a {
display: inline-block;
}
.level {
margin-bottom: 0;
}
.level-item {
max-width: 100%;
}
} }

View File

@ -2,8 +2,8 @@ export const FETCH_ISSUES_REQUEST = "FETCH_ISSUES_REQUEST";
export const FETCH_ISSUES_SUCCESS = "FETCH_ISSUES_SUCCESS"; export const FETCH_ISSUES_SUCCESS = "FETCH_ISSUES_SUCCESS";
export const FETCH_ISSUES_FAILURE = "FETCH_ISSUES_FAILURE"; export const FETCH_ISSUES_FAILURE = "FETCH_ISSUES_FAILURE";
export function fetchIssues(project: any, milestones: string) { export function fetchIssues(project: any) {
return { type: FETCH_ISSUES_REQUEST, project, milestones }; return { type: FETCH_ISSUES_REQUEST, project };
}; };
export const ADD_LABEL_REQUEST = "ADD_LABEL_REQUEST"; export const ADD_LABEL_REQUEST = "ADD_LABEL_REQUEST";

View File

@ -2,8 +2,8 @@ export const BUILD_KANBOARD_REQUEST = "BUILD_KANBOARD_REQUEST";
export const BUILD_KANBOARD_SUCCESS = "BUILD_KANBOARD_SUCCESS"; export const BUILD_KANBOARD_SUCCESS = "BUILD_KANBOARD_SUCCESS";
export const BUILD_KANBOARD_FAILURE = "BUILD_KANBOARD_FAILURE"; export const BUILD_KANBOARD_FAILURE = "BUILD_KANBOARD_FAILURE";
export function buildKanboard(board: string, milestones: string) { export function buildKanboard(board: string) {
return { type: BUILD_KANBOARD_REQUEST, board, milestones }; return { type: BUILD_KANBOARD_REQUEST, board };
}; };
export const MOVE_CARD = "MOVE_CARD"; export const MOVE_CARD = "MOVE_CARD";

View File

@ -2,14 +2,6 @@ export const FETCH_PROJECTS_REQUEST = "FETCH_PROJECTS_REQUEST";
export const FETCH_PROJECTS_SUCCESS = "FETCH_PROJECTS_SUCCESS"; export const FETCH_PROJECTS_SUCCESS = "FETCH_PROJECTS_SUCCESS";
export const FETCH_PROJECTS_FAILURE = "FETCH_PROJECTS_FAILURE"; export const FETCH_PROJECTS_FAILURE = "FETCH_PROJECTS_FAILURE";
export const FETCH_PROJECT_MILESTONES_REQUEST = "FETCH_PROJECTS_MILESTONES_REQUEST";
export const FETCH_PROJECT_MILESTONES_SUCCESS = "FETCH_PROJECTS_MILESTONES_SUCCESS";
export const FETCH_PROJECT_MILESTONES_FAILURE = "FETCH_PROJECTS_MILESTONES_FAILURE";
export function fetchProjects() { export function fetchProjects() {
return { type: FETCH_PROJECTS_REQUEST }; return { type: FETCH_PROJECTS_REQUEST };
};
export function fetchProjectMilestones(project: any) {
return { type: FETCH_PROJECT_MILESTONES_REQUEST, project };
}; };

View File

@ -37,22 +37,22 @@ function handleMoveCard(state: any, action: any) {
const kanboard = state.byID[boardID]; const kanboard = state.byID[boardID];
const columns = [ ...kanboard.columns ]; const lanes = [ ...kanboard.lanes ];
const fromLane = columns[fromLaneID]; const fromLane = lanes[fromLaneID];
const toLane = columns[toLaneID]; const toLane = lanes[toLaneID];
const card = fromLane.cards[fromPosition]; const card = fromLane.cards[fromPosition];
const fromCards = [ ...fromLane.cards ]; const fromCards = [ ...fromLane.cards ];
if (fromLaneID !== toLaneID) { if (fromLaneID !== toLaneID) {
fromCards.splice(fromPosition, 1); fromCards.splice(fromPosition, 1);
columns[fromLaneID] = { lanes[fromLaneID] = {
...fromLane, ...fromLane,
cards: fromCards, cards: fromCards,
}; };
const toCards = [ ...toLane.cards ]; const toCards = [ ...toLane.cards ];
toCards.splice(toPosition, 0, card); toCards.splice(toPosition, 0, card);
columns[toLaneID] = { lanes[toLaneID] = {
...toLane, ...toLane,
cards: toCards, cards: toCards,
}; };
@ -67,7 +67,7 @@ function handleMoveCard(state: any, action: any) {
...state.byID, ...state.byID,
[boardID]: { [boardID]: {
...state.byID[boardID], ...state.byID[boardID],
columns, lanes,
}, },
} }
}; };

View File

@ -1,18 +1,13 @@
import { FETCH_PROJECTS_SUCCESS, FETCH_PROJECT_MILESTONES_SUCCESS } from "../actions/projects"; import { FETCH_PROJECTS_SUCCESS } from "../actions/projects";
export const defaultState = { export const defaultState = {
byName: {}, byName: {},
milestones: ""
}; };
export function projectsReducer(state = defaultState, action: any) { export function projectsReducer(state = defaultState, action: any) {
switch (action.type) { switch(action.type) {
case FETCH_PROJECTS_SUCCESS: case FETCH_PROJECTS_SUCCESS:
return handleFetchProjectsSuccess(state, action); return handleFetchProjectsSuccess(state, action);
case FETCH_PROJECT_MILESTONES_SUCCESS:
return handleFetchProjectMilestonesSuccess(state, action);
default: default:
return state; return state;
} }
@ -29,11 +24,4 @@ function handleFetchProjectsSuccess(state: any, action: any) {
...projectsByName, ...projectsByName,
} }
}; };
}
function handleFetchProjectMilestonesSuccess(state: any, action: any) {
return {
...state,
milestones: action.milestones,
};
} }

View File

@ -1,11 +1,10 @@
import { GiteaUnauthorizedError } from "../../util/gitea"; import { GiteaUnauthorizedError } from "../../util/gitea";
import { put } from 'redux-saga/effects'; import { put } from 'redux-saga/effects';
import { logout } from '../actions/logout'; import { logout } from '../actions/logout';
import { saveReferer } from "../../util/referer";
export function* failuresSaga(action) { export function* failuresSaga(action) {
if (action.error instanceof GiteaUnauthorizedError) { const err = action.error;
saveReferer(); if (err instanceof GiteaUnauthorizedError) {
yield put(logout()); yield put(logout());
} }
} }

View File

@ -1,28 +1,28 @@
import { put, call, retry } from 'redux-saga/effects'; import { put, call, retry } from 'redux-saga/effects';
import { import {
FETCH_ISSUES_SUCCESS, FETCH_ISSUES_FAILURE, FETCH_ISSUES_SUCCESS, FETCH_ISSUES_FAILURE,
ADD_LABEL_FAILURE, ADD_LABEL_SUCCESS, ADD_LABEL_FAILURE, ADD_LABEL_SUCCESS,
REMOVE_LABEL_FAILURE, REMOVE_LABEL_SUCCESS, REMOVE_LABEL_FAILURE, REMOVE_LABEL_SUCCESS,
CREATE_ISSUE_FAILURE, CREATE_ISSUE_SUCCESS CREATE_ISSUE_FAILURE, CREATE_ISSUE_SUCCESS
} from '../actions/issues'; } from '../actions/issues';
import { gitea } from '../../util/gitea'; import { gitea } from '../../util/gitea';
export function* fetchIssuesSaga(action: any) { export function* fetchIssuesSaga(action: any) {
const { project, milestones } = action; const { project } = action;
let issues = []; let issues = [];
try { try {
let page = 1; let page = 1;
while (true) { while(true) {
let pageIssues = yield call(gitea.fetchIssues.bind(gitea), project, page, milestones); let pageIssues = yield call(gitea.fetchIssues.bind(gitea), action.project, page);
if (pageIssues.length === 0) { if (pageIssues.length === 0) {
break; break;
} }
issues.push(...pageIssues.filter(issue => issue.pull_request === null)); issues.push(...pageIssues);
page++; page++;
} }
} catch (error) { } catch(error) {
yield put({ type: FETCH_ISSUES_FAILURE, project, error }); yield put({ type: FETCH_ISSUES_FAILURE, project, error });
return; return;
} }
@ -42,7 +42,7 @@ export function* addLabelSaga(action: any) {
try { try {
yield retry(5, 250, gitea.addIssueLabel.bind(gitea), project, issueNumber, giteaLabel.id); yield retry(5, 250, gitea.addIssueLabel.bind(gitea), project, issueNumber, giteaLabel.id);
} catch (error) { } catch(error) {
yield put({ type: ADD_LABEL_FAILURE, error }); yield put({ type: ADD_LABEL_FAILURE, error });
return; return;
} }
@ -62,11 +62,11 @@ export function* removeLabelSaga(action: any) {
try { try {
yield retry(5, 250, gitea.removeIssueLabel.bind(gitea), project, issueNumber, giteaLabel.id); yield retry(5, 250, gitea.removeIssueLabel.bind(gitea), project, issueNumber, giteaLabel.id);
} catch (error) { } catch(error) {
yield put({ type: REMOVE_LABEL_FAILURE, error }); yield put({ type: REMOVE_LABEL_FAILURE, error });
return; return;
} }
yield put({ type: REMOVE_LABEL_SUCCESS, project, issueNumber, label }); yield put({ type: REMOVE_LABEL_SUCCESS, project, issueNumber, label });
} }
@ -76,14 +76,19 @@ export function* createIssueSaga(action: any) {
const labels = yield call(gitea.fetchProjectLabels.bind(gitea), project); const labels = yield call(gitea.fetchProjectLabels.bind(gitea), project);
const giteaLabel = labels.find((l: any) => l.name === label) const giteaLabel = labels.find((l: any) => l.name === label)
let issue; if (!giteaLabel) {
try { yield put({ type: CREATE_ISSUE_FAILURE, error: new Error(`Label "${label}" not found !`) });
issue = yield call(gitea.createIssue.bind(gitea), project, title, body, giteaLabel ? giteaLabel.id : null);
} catch (error) {
yield put({ type: CREATE_ISSUE_FAILURE, error });
return; return;
} }
let issue;
try {
issue = yield call(gitea.createIssue.bind(gitea), project, title, body, giteaLabel.id);
} catch(error) {
yield put({ type: CREATE_ISSUE_FAILURE, error });
return;
}
yield put({ type: CREATE_ISSUE_SUCCESS, project, title, label, body, issue }); yield put({ type: CREATE_ISSUE_SUCCESS, project, title, label, body, issue });
} }

View File

@ -7,22 +7,22 @@ import { Board, BoardLane } from '../../types/board';
import { KanboardLane, Kanboard, KanboardCard } from '../../types/kanboard'; import { KanboardLane, Kanboard, KanboardCard } from '../../types/kanboard';
export function* moveCardSaga(action: any) { export function* moveCardSaga(action: any) {
const { const {
boardID, fromLaneID, boardID, fromLaneID,
fromPosition, toLaneID, fromPosition, toLaneID,
toPosition, toPosition,
} = action; } = action;
if (fromLaneID === toLaneID) return; if (fromLaneID === toLaneID) return;
const { board, kanboard } = yield select(state => { const { board, kanboard} = yield select(state => {
return { return {
kanboard: state.kanboards.byID[boardID], kanboard: state.kanboards.byID[boardID],
board: state.boards.byID[boardID] board: state.boards.byID[boardID]
} }
}); });
const toLane = kanboard.columns[toLaneID]; const toLane = kanboard.lanes[toLaneID];
const card = toLane.cards[toPosition]; const card = toLane.cards[toPosition];
if (!card) return; if (!card) return;
@ -33,21 +33,21 @@ export function* moveCardSaga(action: any) {
} }
export function* buildKanboardSaga(action: any) { export function* buildKanboardSaga(action: any) {
const { board, milestones } = action; const { board } = action;
console.log("milestones", milestones);
let kanboard; let kanboard;
try { try {
for (let p, i = 0; (p = board.projects[i]); i++) { for (let p, i = 0; (p = board.projects[i]); i++) {
const { project } = yield fetchIssues(p, "test"); const { project } = yield fetchIssues(p);
yield fetchIssuesSaga({ project: project, milestones: milestones }); yield fetchIssuesSaga({ project });
} }
const issues = yield select(state => state.issues); const issues = yield select(state => state.issues);
kanboard = createKanboard(board, issues); kanboard = createKanboard(board, issues);
} catch (error) { } catch(error) {
yield put({ type: BUILD_KANBOARD_FAILURE, error }); yield put({ type: BUILD_KANBOARD_FAILURE, error });
return return
} }
@ -56,14 +56,14 @@ export function* buildKanboardSaga(action: any) {
} }
export function* refreshKanboardSaga(action: any) { export function* refreshKanboardSaga(action: any) {
const { project, milestones } = action; const { project } = action;
const boards = yield select(state => state.boards); const boards = yield select(state => state.boards);
const boardValues = Object.values(boards.byID); const boardValues = Object.values(boards.byID);
for (let b: any, i = 0; (b = boardValues[i]); i++) { for (let b: any, i = 0; (b = boardValues[i]); i++) {
const hasProject = b.projects.indexOf(project) !== -1; const hasProject = b.projects.indexOf(project) !== -1;
if (!hasProject) continue; if (!hasProject) continue;
yield put(buildKanboard(b, milestones)); yield put(buildKanboard(b));
} }
} }
@ -73,19 +73,19 @@ function createCards(projects: Project[], issues: any, lane: BoardLane, rest: Se
const projectIssues = p in issues.byProject ? issues.byProject[p] : []; const projectIssues = p in issues.byProject ? issues.byProject[p] : [];
return projectIssues.reduce((projectCards: KanboardCard[], issue: any) => { return projectIssues.reduce((projectCards: KanboardCard[], issue: any) => {
const hasLabel = issue.labels.some((l: any) => l.name === lane.issueLabel); const hasLabel = issue.labels.some((l: any) => l.name === lane.issueLabel);
const { card, memoized } = getMemoizedKanboardCard(issue.id, issue.title, p, issue); const card = getMemoizedKanboardCard(issue.id, issue.title, p, issue);
if (hasLabel) { if (hasLabel) {
projectCards.push(card); projectCards.push(card);
rest.delete(card); rest.delete(card);
} else { } else {
if (!memoized) rest.add(card); rest.add(card);
} }
return projectCards; return projectCards;
}, laneCards); }, laneCards);
}, []); }, []);
@ -93,21 +93,13 @@ function createCards(projects: Project[], issues: any, lane: BoardLane, rest: Se
return cards; return cards;
} }
const kanboardCardMemo: { [key: string]: KanboardCard } = {}; const kanboardCardMemo: {[key: string]: KanboardCard} = {};
function getKanboardCardMemoizationKey(id: number, project: Project, issue: Issue) { function getMemoizedKanboardCard(id: number, title: string, project: Project, issue: Issue): KanboardCard {
return `${project.id}-${issue.id}-${id}`; const key = `${project.id}-${issue.id}-${id}`;
} if (kanboardCardMemo.hasOwnProperty(key)) return kanboardCardMemo[key];
function isKanboardCardMemoized(key: string) {
return kanboardCardMemo.hasOwnProperty(key)
}
function getMemoizedKanboardCard(id: number, title: string, project: Project, issue: Issue) {
const key = getKanboardCardMemoizationKey(id, project, issue);
if (isKanboardCardMemoized(key)) return { card: kanboardCardMemo[key], memoized: true };
kanboardCardMemo[key] = { id, title, project, issue }; kanboardCardMemo[key] = { id, title, project, issue };
return { card: kanboardCardMemo[key], memoized: false }; return kanboardCardMemo[key];
} }
function resetKandboarCardMemo() { function resetKandboarCardMemo() {
@ -115,9 +107,9 @@ function resetKandboarCardMemo() {
} }
function createKanboardLanes(board: Board, issues: any): KanboardLane[] { function createKanboardLanes(board: Board, issues: any): KanboardLane[] {
const lanes: KanboardLane[] = []; const lanes: KanboardLane[] = [];
const rest = new Set<KanboardCard>(); const rest = new Set<KanboardCard>();
resetKandboarCardMemo(); resetKandboarCardMemo();
board.lanes.forEach((l: BoardLane, i: number) => { board.lanes.forEach((l: BoardLane, i: number) => {
@ -136,7 +128,7 @@ function createKanboardLanes(board: Board, issues: any): KanboardLane[] {
}); });
resetKandboarCardMemo(); resetKandboarCardMemo();
return lanes; return lanes;
} }
@ -145,8 +137,8 @@ function createKanboard(board: Board, issues: any) {
const kanboard = { const kanboard = {
id: board.id, id: board.id,
columns: createKanboardLanes(board, issues), lanes: createKanboardLanes(board, issues),
}; };
return kanboard; return kanboard;
} }

View File

@ -1,5 +1,5 @@
import { put, call } from 'redux-saga/effects'; import { put, call } from 'redux-saga/effects';
import { FETCH_PROJECTS_SUCCESS, FETCH_PROJECTS_FAILURE, FETCH_PROJECT_MILESTONES_FAILURE, FETCH_PROJECT_MILESTONES_SUCCESS } from '../actions/projects'; import { FETCH_PROJECTS_SUCCESS, FETCH_PROJECTS_FAILURE } from '../actions/projects';
import { gitea } from '../../util/gitea'; import { gitea } from '../../util/gitea';
export function* fetchProjectsSaga() { export function* fetchProjectsSaga() {
@ -7,23 +7,10 @@ export function* fetchProjectsSaga() {
let projects; let projects;
try { try {
projects = yield call(gitea.fetchUserProjects.bind(gitea)) projects = yield call(gitea.fetchUserProjects.bind(gitea))
} catch (error) { } catch(error) {
yield put({ type: FETCH_PROJECTS_FAILURE, error }); yield put({ type: FETCH_PROJECTS_FAILURE, error });
return; return;
} }
yield put({ type: FETCH_PROJECTS_SUCCESS, projects }); yield put({ type: FETCH_PROJECTS_SUCCESS, projects });
} }
export function* fetchProjectMilestonesSaga(action: any) {
const { project } = action;
let milestones;
try {
milestones = yield call(gitea.fetchMilestones.bind(gitea), project)
} catch (error) {
yield put({ type: FETCH_PROJECT_MILESTONES_FAILURE, error });
return;
}
yield put({ type: FETCH_PROJECT_MILESTONES_SUCCESS, milestones });
}

View File

@ -1,9 +0,0 @@
import { hasReferer, getReferer, clearReferer } from '../../util/referer';
export function* navigateToRefererSaga() {
if (!hasReferer()) return;
const referer = getReferer();
console.log("Redirecting to referer", referer);
clearReferer();
window.location.hash = referer;
}

View File

@ -4,17 +4,15 @@ import { FETCH_BOARDS_REQUEST, SAVE_BOARD_REQUEST, DELETE_BOARD_REQUEST } from '
import { fetchBoardsSaga, saveBoardSaga, deleteBoardSaga } from './boards'; import { fetchBoardsSaga, saveBoardSaga, deleteBoardSaga } from './boards';
import { FETCH_ISSUES_REQUEST, ADD_LABEL_REQUEST, REMOVE_LABEL_REQUEST, CREATE_ISSUE_REQUEST, CREATE_ISSUE_SUCCESS } from '../actions/issues'; import { FETCH_ISSUES_REQUEST, ADD_LABEL_REQUEST, REMOVE_LABEL_REQUEST, CREATE_ISSUE_REQUEST, CREATE_ISSUE_SUCCESS } from '../actions/issues';
import { fetchIssuesSaga, addLabelSaga, removeLabelSaga, createIssueSaga } from './issues'; import { fetchIssuesSaga, addLabelSaga, removeLabelSaga, createIssueSaga } from './issues';
import { FETCH_PROJECTS_REQUEST, FETCH_PROJECT_MILESTONES_REQUEST } from '../actions/projects'; import { FETCH_PROJECTS_REQUEST } from '../actions/projects';
import { fetchProjectMilestonesSaga, fetchProjectsSaga } from './projects'; import { fetchProjectsSaga } from './projects';
import { LOGOUT_REQUEST, LOGOUT_SUCCESS } from '../actions/logout'; import { LOGOUT_REQUEST, LOGOUT_SUCCESS } from '../actions/logout';
import { logoutSaga, logoutSuccessSaga } from './logout'; import { logoutSaga, logoutSuccessSaga } from './logout';
import { BUILD_KANBOARD_REQUEST, MOVE_CARD } from '../actions/kanboards'; import { BUILD_KANBOARD_REQUEST, MOVE_CARD } from '../actions/kanboards';
import { buildKanboardSaga, moveCardSaga, refreshKanboardSaga } from './kanboards'; import { buildKanboardSaga, moveCardSaga, refreshKanboardSaga } from './kanboards';
import { navigateToRefererSaga } from './referer';
export function* rootSaga() { export function* rootSaga() {
yield all([ yield all([
navigateToRefererSaga(),
takeEvery(patternFromRegExp(/^.*_FAILURE/), failuresSaga), takeEvery(patternFromRegExp(/^.*_FAILURE/), failuresSaga),
takeLatest(FETCH_BOARDS_REQUEST, fetchBoardsSaga), takeLatest(FETCH_BOARDS_REQUEST, fetchBoardsSaga),
takeLatest(BUILD_KANBOARD_REQUEST, buildKanboardSaga), takeLatest(BUILD_KANBOARD_REQUEST, buildKanboardSaga),
@ -22,7 +20,6 @@ export function* rootSaga() {
takeLatest(DELETE_BOARD_REQUEST, deleteBoardSaga), takeLatest(DELETE_BOARD_REQUEST, deleteBoardSaga),
takeLatest(FETCH_ISSUES_REQUEST, fetchIssuesSaga), takeLatest(FETCH_ISSUES_REQUEST, fetchIssuesSaga),
takeLatest(FETCH_PROJECTS_REQUEST, fetchProjectsSaga), takeLatest(FETCH_PROJECTS_REQUEST, fetchProjectsSaga),
takeLatest(FETCH_PROJECT_MILESTONES_REQUEST, fetchProjectMilestonesSaga),
takeEvery(MOVE_CARD, moveCardSaga), takeEvery(MOVE_CARD, moveCardSaga),
takeEvery(ADD_LABEL_REQUEST, addLabelSaga), takeEvery(ADD_LABEL_REQUEST, addLabelSaga),
takeEvery(REMOVE_LABEL_REQUEST, removeLabelSaga), takeEvery(REMOVE_LABEL_REQUEST, removeLabelSaga),
@ -33,7 +30,7 @@ export function* rootSaga() {
]); ]);
} }
export function patternFromRegExp(re: any) { export function patternFromRegExp(re: any) {
return (action: any) => { return (action: any) => {
return re.test(action.type); return re.test(action.type);
}; };

View File

@ -1,4 +1,4 @@
import { createStore, applyMiddleware, compose } from 'redux' import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga' import createSagaMiddleware from 'redux-saga'
import { rootReducer } from './reducers/root' import { rootReducer } from './reducers/root'
import { rootSaga } from './sagas/root' import { rootSaga } from './sagas/root'
@ -14,8 +14,6 @@ if (process.env.NODE_ENV !== 'production') {
reduxMiddlewares.push(loggerMiddleware); reduxMiddlewares.push(loggerMiddleware);
} }
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// create the saga middleware // create the saga middleware
const sagaMiddleware = createSagaMiddleware() const sagaMiddleware = createSagaMiddleware()
reduxMiddlewares.push(sagaMiddleware); reduxMiddlewares.push(sagaMiddleware);
@ -23,7 +21,7 @@ reduxMiddlewares.push(sagaMiddleware);
// mount it on the Store // mount it on the Store
export const store = createStore( export const store = createStore(
rootReducer, rootReducer,
composeEnhancers(applyMiddleware(...reduxMiddlewares)), applyMiddleware(...reduxMiddlewares)
) )
// then run the saga // then run the saga

View File

@ -2,26 +2,17 @@
export class GiteaUnauthorizedError extends Error { export class GiteaUnauthorizedError extends Error {
constructor(...args: any[]) { constructor(...args: any[]) {
super(...args) super(...args)
Object.setPrototypeOf(this, GiteaUnauthorizedError.prototype);
} }
} }
export class GiteaClient { export class GiteaClient {
fetchIssues(project: any, page = 1, milestones = "") { fetchIssues(project: any, page = 1) {
return fetch(`/gitea/api/v1/repos/${project}/issues?page=${page}&milestones=${milestones}`) return fetch(`/gitea/api/v1/repos/${project}/issues?page=${page}`)
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
.then(res => res.json()) .then(res => res.json())
; ;
}
fetchMilestones(project: any) {
return fetch(`/gitea/api/v1/repos/${project}/milestones`)
.then(this.assertAuthorization)
.then(this.assertOk)
.then(res => res.json())
;
} }
fetchUserProjects() { fetchUserProjects() {
@ -29,7 +20,7 @@ export class GiteaClient {
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
.then(res => res.json()) .then(res => res.json())
; ;
} }
addIssueLabel(project: any, issueNumber: any, labelID: any) { addIssueLabel(project: any, issueNumber: any, labelID: any) {
@ -40,9 +31,9 @@ export class GiteaClient {
}, },
body: JSON.stringify({ labels: [labelID] }), body: JSON.stringify({ labels: [labelID] }),
}) })
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
.then(res => res.json()) .then(res => res.json())
} }
fetchProjectLabels(project: any) { fetchProjectLabels(project: any) {
@ -50,15 +41,15 @@ export class GiteaClient {
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
.then(res => res.json()) .then(res => res.json())
; ;
} }
removeIssueLabel(project: any, issueNumber: any, labelID: any) { removeIssueLabel(project: any, issueNumber: any, labelID: any) {
return fetch(`/gitea/api/v1/repos/${project}/issues/${issueNumber}/labels/${labelID}`, { return fetch(`/gitea/api/v1/repos/${project}/issues/${issueNumber}/labels/${labelID}`, {
method: 'DELETE' method: 'DELETE'
}) })
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
} }
createIssue(project: any, title: any, body: any, labelID: any) { createIssue(project: any, title: any, body: any, labelID: any) {
@ -70,12 +61,12 @@ export class GiteaClient {
body: JSON.stringify({ body: JSON.stringify({
title, title,
body, body,
labels: labelID ? [labelID] : undefined, labels: [labelID],
}), }),
}) })
.then(this.assertAuthorization) .then(this.assertAuthorization)
.then(this.assertOk) .then(this.assertOk)
.then(res => res.json()) .then(res => res.json())
} }
assertOk(res: any) { assertOk(res: any) {

View File

@ -1,19 +0,0 @@
const localStorage = window.localStorage;
const refererKey = 'referer';
export function getReferer() {
return localStorage.getItem(refererKey);
}
export function saveReferer() {
console.log("Saving referer", window.location.hash);
localStorage.setItem(refererKey, window.location.hash);
}
export function hasReferer() {
return !!getReferer();
}
export function clearReferer() {
localStorage.removeItem(refererKey);
}

View File

@ -19,7 +19,7 @@ module.exports = {
}, },
module: { module: {
rules: [{ rules: [{
test: /\.s(a|c)ss$/, test: /\.scss$/,
use: [ use: [
MiniCssExtractPlugin.loader, MiniCssExtractPlugin.loader,
{ {

View File

@ -28,7 +28,7 @@ func getServiceContainer(conf *config.Config) (*service.Container, error) {
ctn.Provide( ctn.Provide(
session.ServiceName, session.ServiceName,
gorilla.ServiceProvider("gengitkan", cookieStore), gorilla.ServiceProvider("gitea-kan", cookieStore),
) )
// Create and expose config service provider // Create and expose config service provider

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

14
debian/control vendored Normal file
View File

@ -0,0 +1,14 @@
Source: gitea-kan
Section: unknown
Priority: optional
Maintainer: William Petit <wpetit@cadoles.com>
Build-Depends: debhelper (>= 8.0.0), wget, ca-certificates, tar
Standards-Version: 3.9.4
Homepage: http://forge.cadoles.com/wpetit/gitea-kan
Vcs-Git: http://forge.cadoles.com/wpetit/gitea-kan.git
Vcs-Browser: http://forge.cadoles.com/wpetit/gitea-kan
Package: gitea-kan
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Application type "Kanboard" connectée à Gitea

1
debian/gitea-kan.dirs vendored Normal file
View File

@ -0,0 +1 @@
var/lib/gitea-kan

13
debian/gitea-kan.service vendored Normal file
View File

@ -0,0 +1,13 @@
[Unit]
Description=Application type "Kanboard" connectée à Gitea
After=network-online.target
[Service]
Type=simple
Environment=GITEAKAN_HTTP_PUBDIR=/usr/share/gitea-kan/public
Environment=GITEAKAN_DATA_DBPATH=/var/lib/gitea-kan/data.db
ExecStart=/usr/bin/gitea-kan -workdir /usr/share/gitea-kan -config /etc/gitea-kan/server.conf
Restart=on-failure
[Install]
WantedBy=multi-user.target

54
debian/rules vendored Normal file
View File

@ -0,0 +1,54 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
GO_VERSION := 1.13.5
OS := linux
ARCH := amd64
GOPATH=$(HOME)/go
ifeq (, $(shell which go 2>/dev/null))
override_dh_auto_build: install-go
endif
ifeq (, $(shell which node 2>/dev/null))
override_dh_auto_build: install-nodejs
endif
%:
dh $@ --with systemd
override_dh_auto_build: $(GOPATH)
cd client && npm install
GOPATH=$(GOPATH) PATH="$(PATH):/usr/local/go/bin:$(GOPATH)/bin" go mod vendor
GOPATH=$(GOPATH) PATH="$(PATH):/usr/local/go/bin:$(GOPATH)/bin" ARCH_TARGETS=$(ARCH) make release
$(GOPATH):
mkdir -p $(GOPATH)
install-go:
wget https://dl.google.com/go/go$(GO_VERSION).$(OS)-$(ARCH).tar.gz
tar -C /usr/local -xzf go$(GO_VERSION).$(OS)-$(ARCH).tar.gz
install-nodejs:
wget -O- https://deb.nodesource.com/setup_12.x | bash -
apt-get install -y nodejs
override_dh_auto_install:
mkdir -p debian/gitea-kan/usr/share/gitea-kan
mkdir -p debian/gitea-kan/etc/gitea-kan
mkdir -p debian/gitea-kan/usr/bin
cp -r release/server-$(OS)-$(ARCH)/* debian/gitea-kan/usr/share/gitea-kan/
mv debian/gitea-kan/usr/share/gitea-kan/bin/server debian/gitea-kan/usr/bin/gitea-kan
mv debian/gitea-kan/usr/share/gitea-kan/server.conf debian/gitea-kan/etc/gitea-kan/server.conf
install -d debian/gitea-kan
override_dh_strip:
override_dh_auto_test:

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)