Skip to content
28 changes: 28 additions & 0 deletions src/actions/__test__/program.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,34 @@ describe('Program actions', () => {
mock.restore();
});

test('fetch featured programs', async () => {
const mock = new MockAdapter(axios);
const programs = [{
id: 33,
name: 'Unnamed_Design_3',
content: '<xml><variables></variables></xml>',
admin_tags: ['featured'],
}];

mock.onGet('/api/v1/block-diagrams/', {
params: {
admin_tags: ['featured'],
},
}).reply(200, programs);

const action = fetchPrograms({
params: {
admin_tags: ['featured'],
},
});
const { type } = action;
const payload = await action.payload;

expect(type).toEqual('FETCH_FEATURED_PROGRAMS');
expect(payload).toEqual(programs);
mock.restore();
});

test('remove program', async () => {
const mock = new MockAdapter(axios);

Expand Down
6 changes: 6 additions & 0 deletions src/actions/program.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export const FETCH_USER_PROGRAMS = 'FETCH_USER_PROGRAMS';
export const FETCH_USER_PROGRAMS_PENDING = `${FETCH_USER_PROGRAMS}_PENDING`;
export const FETCH_USER_PROGRAMS_FULFILLED = `${FETCH_USER_PROGRAMS}_FULFILLED`;
export const FETCH_USER_PROGRAMS_REJECTED = `${FETCH_USER_PROGRAMS}_REJECTED`;
export const FETCH_FEATURED_PROGRAMS = 'FETCH_FEATURED_PROGRAMS';
export const FETCH_FEATURED_PROGRAMS_PENDING = `${FETCH_FEATURED_PROGRAMS}_PENDING`;
export const FETCH_FEATURED_PROGRAMS_FULFILLED = `${FETCH_FEATURED_PROGRAMS}_FULFILLED`;
export const FETCH_FEATURED_PROGRAMS_REJECTED = `${FETCH_FEATURED_PROGRAMS}_REJECTED`;
export const REMOVE_PROGRAM = 'REMOVE_PROGRAM';
export const REMOVE_PROGRAM_PENDING = `${REMOVE_PROGRAM}_PENDING`;
export const REMOVE_PROGRAM_FULFILLED = `${REMOVE_PROGRAM}_FULFILLED`;
Expand All @@ -22,6 +26,8 @@ export const fetchPrograms = (xhrOptions) => {

if (xhrOptions && xhrOptions.params && xhrOptions.params.user) {
type = FETCH_USER_PROGRAMS;
} else if (xhrOptions && xhrOptions.params && xhrOptions.params.admin_tags) {
type = FETCH_FEATURED_PROGRAMS;
}

return ({
Expand Down
24 changes: 15 additions & 9 deletions src/components/ProgramCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ProgramCollection extends Component {
}

update = () => {
const { onUpdate, owned } = this.props;
const { onUpdate } = this.props;
const {
page,
ordering,
Expand All @@ -45,7 +45,7 @@ class ProgramCollection extends Component {
params.search = searchQuery;
}

onUpdate(params, owned);
onUpdate(params);
}

toggleOrdering = (name) => {
Expand Down Expand Up @@ -81,11 +81,11 @@ class ProgramCollection extends Component {

render() {
const {
user,
label,
onProgramClick,
onRemoveClick,
programs,
owned,
intl,
tag,
} = this.props;
Expand Down Expand Up @@ -182,7 +182,7 @@ class ProgramCollection extends Component {
</Card.Header>
<Card.Meta>
{
owned ? (
user.username === program.user.username ? (
<FormattedMessage
id="app.program_collection.mine"
description="Label to indicate program owned by user"
Expand All @@ -193,9 +193,14 @@ class ProgramCollection extends Component {
</Card.Meta>
</Card.Content>
<Card.Content extra>
<Button primary id={program.id} data-owned={owned} onClick={onProgramClick}>
<Button
primary
id={program.id}
data-owned={user.username === program.user.username}
onClick={onProgramClick}
>
{
owned ? (
user.username === program.user.username ? (
<FormattedMessage
id="app.program_collection.work"
description="Button label to keep working on program"
Expand All @@ -211,7 +216,7 @@ class ProgramCollection extends Component {
}
</Button>
{
owned ? (
user.username === program.user.username ? (
<Button
negative
id={program.id}
Expand Down Expand Up @@ -251,13 +256,15 @@ class ProgramCollection extends Component {
}

ProgramCollection.defaultProps = {
owned: false,
tag: {
tags: [],
},
};

ProgramCollection.propTypes = {
user: PropTypes.shape({
username: PropTypes.string.isRequired,
}).isRequired,
programs: PropTypes.shape({
next: PropTypes.string,
previous: PropTypes.string,
Expand All @@ -278,7 +285,6 @@ ProgramCollection.propTypes = {
})),
}),
label: PropTypes.string.isRequired,
owned: PropTypes.bool,
onProgramClick: PropTypes.func.isRequired,
onRemoveClick: PropTypes.func.isRequired,
onUpdate: PropTypes.func.isRequired,
Expand Down
83 changes: 66 additions & 17 deletions src/components/ProgramList.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ProgramList extends Component {
user: user.user_id,
}).then(() => fetchPrograms({
user__not: user.user_id,
})).then(() => fetchPrograms({
admin_tags: 'featured',
})).then(() => fetchTags());
}

Expand All @@ -67,6 +69,9 @@ class ProgramList extends Component {
}))
.then(() => fetchPrograms({
user__not: user.user_id,
}))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since a user should never be able to remove a featured program, this seems unnecessary. The same could be said for the user__not above

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Removing them both.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, apparently they are in fact needed!

.then(() => fetchPrograms({
admin_tags: 'featured',
}));
}

Expand All @@ -87,32 +92,40 @@ class ProgramList extends Component {
});
}

fetch = (params, owned) => {
fetchUserPrograms = (params) => {
const { fetchPrograms, user } = this.props;
fetchPrograms({
user: user.user_id,
...params,
});
}

if (owned) {
fetchPrograms({
user: user.user_id,
...params,
});
} else {
fetchPrograms({
user__not: user.user_id,
...params,
});
}
fetchFeaturedPrograms = (params) => {
const { fetchPrograms } = this.props;
fetchPrograms({
admin_tags: 'featured',
...params,
});
}

fetchOtherPrograms = (params) => {
const { fetchPrograms, user } = this.props;
fetchPrograms({
user__not: user.user_id,
...params,
});
}

programSegment = (programs, tag, label, owned) => (
programSegment = (programs, user, tag, label, onUpdate) => (
<Segment raised style={{ margin: '10px 10% 10px 10%' }}>
<ProgramCollection
programs={programs}
user={user}
tag={tag}
label={label}
owned={owned}
onProgramClick={this.loadProgram}
onRemoveClick={this.showConfirm}
onUpdate={this.fetch}
onUpdate={onUpdate}
/>
</Segment>
)
Expand All @@ -121,8 +134,10 @@ class ProgramList extends Component {
const {
intl,
programs,
user,
tag,
userPrograms,
featuredPrograms,
} = this.props;
const {
confirmOpen,
Expand All @@ -136,6 +151,12 @@ class ProgramList extends Component {
defaultMessage: 'My Programs',
});

const featuredProgramsHeader = intl.formatMessage({
id: 'app.program_list.featured_programs',
description: 'Header for all featured programs',
defaultMessage: 'Featured Programs',
});

const otherProgramsHeader = intl.formatMessage({
id: 'app.program_list.other_programs',
description: 'Header for finding other user\'s programs',
Expand Down Expand Up @@ -189,12 +210,19 @@ class ProgramList extends Component {
{
userPrograms === null
? (<Loader active />)
: this.programSegment(userPrograms, tag, myProgramsHeader, true)
: this.programSegment(userPrograms, user, tag,
myProgramsHeader, this.fetchUserPrograms)
}
{
featuredPrograms === null
? (<Loader active />)
: this.programSegment(featuredPrograms, user, tag,
featuredProgramsHeader, this.fetchFeaturedPrograms)
}
{
programs === null
? (<Loader active />)
: this.programSegment(programs, tag, otherProgramsHeader, false)
: this.programSegment(programs, user, tag, otherProgramsHeader, this.fetchOtherPrograms)
}
<Confirm
header={dialogHeader}
Expand Down Expand Up @@ -223,6 +251,12 @@ ProgramList.defaultProps = {
total_pages: 1,
results: [],
},
featuredPrograms: {
next: null,
previous: null,
total_pages: 1,
results: [],
},
tag: {
tags: [],
},
Expand All @@ -237,6 +271,7 @@ ProgramList.propTypes = {
clearProgram: PropTypes.func.isRequired,
user: PropTypes.shape({
user_id: PropTypes.number.isRequired,
username: PropTypes.string.isRequired,
}).isRequired,
programs: PropTypes.shape({
next: PropTypes.string,
Expand Down Expand Up @@ -266,6 +301,20 @@ ProgramList.propTypes = {
}),
),
}),
featuredPrograms: PropTypes.shape({
next: PropTypes.string,
previous: PropTypes.string,
total_pages: PropTypes.number,
results: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
user: PropTypes.shape({
username: PropTypes.string.isRequired,
}).isRequired,
}),
),
}),
tag: PropTypes.shape({
tags: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
Expand Down
Loading