diff --git a/apps/src/templates/projects/PersonalProjectsTable.jsx b/apps/src/templates/projects/PersonalProjectsTable.jsx index 38d88b2dc1002..6f0a88a85746b 100644 --- a/apps/src/templates/projects/PersonalProjectsTable.jsx +++ b/apps/src/templates/projects/PersonalProjectsTable.jsx @@ -11,9 +11,8 @@ import { personalProjectDataPropType, FEATURED_PROJECT_TYPE_MAP, } from './projectConstants'; -import QuickActionsCell from '../tables/QuickActionsCell'; import {tableLayoutStyles, sortableOptions} from "../tables/tableConstants"; -import PopUpMenu, {MenuBreak} from "@cdo/apps/lib/ui/PopUpMenu"; +import PersonalProjectsTableActionsCell from './PersonalProjectsTableActionsCell'; const PROJECT_DEFAULT_IMAGE = '/blockly/media/projects/project_default.png'; @@ -69,9 +68,6 @@ export const styles = { justifyContent: 'center', alignItems: 'center', }, - xIcon: { - paddingRight: 5, - }, }; // Cell formatters. @@ -96,40 +92,11 @@ const nameFormatter = (projectName, {rowData}) => { const actionsFormatter = (actions, {rowData}) => { return ( - - console.log("Rename was clicked")} - > - {i18n.rename()} - - console.log("Remix was clicked")} - > - {i18n.remix()} - - {!!rowData.isPublished && ( - console.log("Unpublish was clicked")} - > - {i18n.unpublish()} - - )} - {!rowData.isPublished && ( - console.log("Publish was clicked")} - > - {i18n.publish()} - - )} - - console.log("Delete was clicked")} - color={color.red} - > - - {i18n.delete()} - - + ); }; @@ -148,7 +115,7 @@ const publishedAtFormatter = (publishedAt) => { class PersonalProjectsTable extends React.Component { static propTypes = { - personalProjectsList: PropTypes.arrayOf(personalProjectDataPropType).isRequired + personalProjectsList: PropTypes.arrayOf(personalProjectDataPropType).isRequired, }; state = { diff --git a/apps/src/templates/projects/PersonalProjectsTable.story.jsx b/apps/src/templates/projects/PersonalProjectsTable.story.jsx index 6b3414364d828..2a5db47af3a1d 100644 --- a/apps/src/templates/projects/PersonalProjectsTable.story.jsx +++ b/apps/src/templates/projects/PersonalProjectsTable.story.jsx @@ -5,6 +5,7 @@ import {stubFakePersonalProjectData} from './generateFakeProjects'; export default storybook => { storybook .storiesOf('Projects/PersonalProjectsTable', module) + .withReduxStore() .addStoryTable([ { name: 'Personal Project Table', diff --git a/apps/src/templates/projects/PersonalProjectsTableActionsCell.jsx b/apps/src/templates/projects/PersonalProjectsTableActionsCell.jsx new file mode 100644 index 0000000000000..3a6088479a5e5 --- /dev/null +++ b/apps/src/templates/projects/PersonalProjectsTableActionsCell.jsx @@ -0,0 +1,80 @@ +import React, {Component, PropTypes} from 'react'; +import {connect} from 'react-redux'; +import QuickActionsCell from "../tables/QuickActionsCell"; +import PopUpMenu, {MenuBreak} from "@cdo/apps/lib/ui/PopUpMenu"; +import color from "../../util/color"; +import FontAwesome from '../FontAwesome'; +import i18n from '@cdo/locale'; +import {showPublishDialog} from './publishDialog/publishDialogRedux'; + +export const styles = { + xIcon: { + paddingRight: 5, + }, +}; + +class PersonalProjectsTableActionsCell extends Component { + static propTypes = { + isPublished: PropTypes.bool.isRequired, + projectId: PropTypes.string.isRequired, + projectType: PropTypes.string.isRequired, + showPublishDialog: PropTypes.func.isRequired, + }; + + state = { + deleting: false, + publishing: false, + unpublishing: false, + renaming: false, + remixing: false + }; + + onPublish = () => { + this.props.showPublishDialog(this.props.projectId, this.props.projectType); + }; + + render() { + return ( + + console.log("Rename was clicked")} + > + {i18n.rename()} + + console.log("Remix was clicked")} + > + {i18n.remix()} + + {this.props.isPublished && ( + console.log("Unpublish was clicked")} + > + {i18n.unpublish()} + + )} + {!this.props.isPublished && ( + + {i18n.publish()} + + )} + + console.log("Delete was clicked")} + color={color.red} + > + + {i18n.delete()} + + + ); + } +} + +export default connect(state => ({}), dispatch => ({ + showPublishDialog(projectId, projectType) { + dispatch(showPublishDialog(projectId, projectType)); + }, +}))(PersonalProjectsTableActionsCell); diff --git a/apps/src/templates/projects/projectsRedux.js b/apps/src/templates/projects/projectsRedux.js index 41b0bf4c4ec17..2681c24867d2a 100644 --- a/apps/src/templates/projects/projectsRedux.js +++ b/apps/src/templates/projects/projectsRedux.js @@ -2,7 +2,7 @@ import { combineReducers } from 'redux'; import _ from 'lodash'; import { Galleries } from './projectConstants'; - +import {PUBLISH_SUCCESS} from './publishDialog/publishDialogRedux'; // Action types const TOGGLE_GALLERY = 'projects/TOGGLE_GALLERY'; @@ -92,6 +92,18 @@ function personalProjectsList(state = initialPersonalProjectsList, action) { ...state, projects: action.personalProjectsList, }; + case PUBLISH_SUCCESS: + var channelOfInterest = action.lastPublishedProjectData.channel; + + var publishedProjectIndex = state.projects.findIndex(project => project.channel === channelOfInterest); + + var updatedProjects = [...state.projects]; + updatedProjects[publishedProjectIndex].publishedAt = action.lastPublishedAt; + + return { + ...state, + projects: updatedProjects + }; default: return state; } @@ -151,3 +163,8 @@ export function setHasOlderProjects(hasOlderProjects, projectType) { export function setPersonalProjectsList(personalProjectsList) { return {type: SET_PERSONAL_PROJECTS_LIST, personalProjectsList}; } + +export function publishSuccess(lastPublishedAt, lastPublishedProjectData) { + return {type: PUBLISH_SUCCESS, lastPublishedAt, + lastPublishedProjectData}; +} diff --git a/apps/src/templates/projects/publishDialog/publishDialogRedux.js b/apps/src/templates/projects/publishDialog/publishDialogRedux.js index fb20c3f6e3b53..9db4eb75e820d 100644 --- a/apps/src/templates/projects/publishDialog/publishDialogRedux.js +++ b/apps/src/templates/projects/publishDialog/publishDialogRedux.js @@ -6,9 +6,9 @@ import { AllPublishableProjectTypes } from '../../../util/sharedConstants'; const SHOW_PUBLISH_DIALOG = 'publishDialog/SHOW_PUBLISH_DIALOG'; const HIDE_PUBLISH_DIALOG = 'publishDialog/HIDE_PUBLISH_DIALOG'; -export const PUBLISH_REQUEST = 'shareDialog/PUBLISH_REQUEST'; -export const PUBLISH_SUCCESS = 'shareDialog/PUBLISH_SUCCESS'; -export const PUBLISH_FAILURE = 'shareDialog/PUBLISH_FAILURE'; +export const PUBLISH_REQUEST = 'publishDialog/PUBLISH_REQUEST'; +export const PUBLISH_SUCCESS = 'publishDialog/PUBLISH_SUCCESS'; +export const PUBLISH_FAILURE = 'publishDialog/PUBLISH_FAILURE'; // Reducer diff --git a/apps/test/unit/templates/projects/PersonalProjectsTableTest.js b/apps/test/unit/templates/projects/PersonalProjectsTableTest.js index 8cc9ce2536127..f0126c487e062 100644 --- a/apps/test/unit/templates/projects/PersonalProjectsTableTest.js +++ b/apps/test/unit/templates/projects/PersonalProjectsTableTest.js @@ -1,27 +1,31 @@ import React from 'react'; import {mount} from 'enzyme'; import {expect} from '../../../util/configuredChai'; +import {Provider} from 'react-redux'; +import {getStore} from '@cdo/apps/redux'; import {UnconnectedPersonalProjectsTable as PersonalProjectsTable} from '@cdo/apps/templates/projects/PersonalProjectsTable'; import {stubFakePersonalProjectData} from '@cdo/apps/templates/projects/generateFakeProjects'; describe('PersonalProjectsTable', () => { it('renders a table', () => { const wrapper = mount( - + + + ); - expect(wrapper.find('table').exists()).to.be.true; }); it('renders projects as table rows', () => { const wrapper = mount( - + + + ); - const projectRows = wrapper.find('tbody').find('tr'); expect(projectRows).to.have.length(stubFakePersonalProjectData.length); }); diff --git a/apps/test/unit/templates/projects/projectsReduxTest.js b/apps/test/unit/templates/projects/projectsReduxTest.js index 56655523f8b9c..e39c67aaf1a2d 100644 --- a/apps/test/unit/templates/projects/projectsReduxTest.js +++ b/apps/test/unit/templates/projects/projectsReduxTest.js @@ -1,6 +1,7 @@ import { assert } from '../../../util/configuredChai'; import projects, { setPersonalProjectsList, + publishSuccess, } from '@cdo/apps/templates/projects/projectsRedux'; import {stubFakePersonalProjectData} from '@cdo/apps/templates/projects/generateFakeProjects'; @@ -15,4 +16,14 @@ describe('projectsRedux', () => { }); }); + describe('publishSuccess', () => { + it('sets the publishedAt field for the recently published project', () => { + const action = setPersonalProjectsList(stubFakePersonalProjectData); + const nextState = projects(initialState, action); + const nextAction = publishSuccess('2016-11-30T23:59:59.999-08:00', {channel: 'abcd2'}); + const nextNextState = projects(nextState, nextAction); + assert.deepEqual(nextNextState.personalProjectsList.projects[1].channel, 'abcd2'); + assert.deepEqual(nextNextState.personalProjectsList.projects[1].publishedAt, '2016-11-30T23:59:59.999-08:00'); + }); + }); }); diff --git a/dashboard/db/schema.rb b/dashboard/db/schema.rb index d6d01f31feace..85c473ec9c5d0 100644 --- a/dashboard/db/schema.rb +++ b/dashboard/db/schema.rb @@ -89,13 +89,13 @@ end create_table "blocks", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci" do |t| - t.string "name", null: false - t.string "pool", null: false + t.string "name", null: false + t.string "pool", default: "", null: false t.string "category" t.text "config", limit: 65535 t.text "helper_code", limit: 65535 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.datetime "deleted_at" t.index ["deleted_at"], name: "index_blocks_on_deleted_at", using: :btree t.index ["pool", "name"], name: "index_blocks_on_pool_and_name", unique: true, using: :btree diff --git a/pegasus/sites.v3/code.org/public/circuitplayground.md b/pegasus/sites.v3/code.org/public/circuitplayground.md index 4d1e1acbe65bc..6250e756447ba 100644 --- a/pegasus/sites.v3/code.org/public/circuitplayground.md +++ b/pegasus/sites.v3/code.org/public/circuitplayground.md @@ -76,7 +76,7 @@ If you meet **all** of the eligibility requirements, you will qualify for a subs * **For teachers in schools with 40% or greater free/reduced price meals,** the classroom kit will be 100% subsidized, resulting in no cost to the teacher/school, including shipping. * **For teachers at schools with less than 40% free/reduced price meals,** the classroom kit will not be subsidized. Teachers will be expected to pay for the full cost of the kit (approx. $350), though this price will be reduced by a 10% discount for educators to an approximate total of $315. To get the educator discount, teachers must apply the code ADAEDU at checkout. -If a teacher has a smaller class size and does not qualify for or need a full 15 board kit (which supports 30 students), they may purchase a Code.org Circuit Playground Individual Kit. The Individual Kit is designed for a single student or share-pair, costs $29.95, and is also eligible for the 10% educator discount. +If a teacher has a smaller class size and does not qualify for or need a full 15 board kit (which supports 30 students), they may purchase a Code.org Circuit Playground Individual Kit. The Individual Kit is designed for a single student or share-pair, costs $29.95. **In January 2019,** teachers will receive an email from us with more information about how to request a subsidy for the Adafruit Circuit Playground classroom kit on our website. In the meantime, qualified teachers should make sure they're on track in the 2018-19 school year to complete the first semester of CS Discoveries (Units 1-3) with their students. diff --git a/pegasus/sites.v3/hourofcode.com/public/international-partners.md b/pegasus/sites.v3/hourofcode.com/public/international-partners.md index 2ad8c37c74165..0758903772b88 100644 --- a/pegasus/sites.v3/hourofcode.com/public/international-partners.md +++ b/pegasus/sites.v3/hourofcode.com/public/international-partners.md @@ -32,9 +32,9 @@ You, too, can play a leading role to get more people in your country involved! I | Bangladesh | CodeLab | Enayet Chowdhury | www.codelab-online.com | | Brazil | Programaê! - Fundaçao Lemann | | http://programae.org.br/horadocodigo | | Cameroon | Big IT Company Ltd | | | -| Canada | Brilliant Labs Inc. | | https://www.brilliantlabs.ca/ | +| Canada | Brilliant Labs Inc. | | https://www.brilliantlabs.ca | | Canada | Hatch Canada | | www.hatchcanada.com | -| Canada | Canada Learning Code | Carolyn Van | https://www.canadalearningcode.ca/ | +| Canada | Canada Learning Code | Carolyn Van | https://www.canadalearningcode.ca | | Canada | Actua | Tracy Ross | www.actua.ca | | Canada | Kids & Code | | www.kidsandcode.org
hello@kidsandcode.org | | Canada | Kids Code Jeunesse | Bry LeBlanc | http://kidscodejeunesse.org
contact@kidscodejeunesse.org | @@ -43,17 +43,17 @@ You, too, can play a leading role to get more people in your country involved! I | Canada | Sault Ste. Marie innovation Centre | | https://ssmic.com/ | | Chile | Fundación Kodea | Claudia Jaña | http://horadelcodigo.cl
cjana@kodea.org | | China | 码趣学院CodingMarch | Delia Wu | www.codingmarch.com
delia.wu@codingmarch.com | -| China | Classroom Aid, Inc. | | http://classroom-aid.com/CS4Good/ | -| Colombia (also Brazil, Mexico, Spain) | World Tech Makers | Ilana Milkes | http://worldtechmakers.com/
ilana@worldtechmakers.com | -| Czech Republic | Czechitas | Pavla Randakova | https://czechitas.cz/en/
paja@czechitas.cz | +| China | Classroom Aid, Inc. | | http://classroom-aid.com/CS4Good | +| Colombia (also Brazil, Mexico, Spain) | World Tech Makers | Ilana Milkes | http://worldtechmakers.com
ilana@worldtechmakers.com | +| Czech Republic | Czechitas | Pavla Randakova | https://czechitas.cz
paja@czechitas.cz | | Egypt | Q-STEAM Club | Tamer Ragab | http://q-steam.com
learn@q-steam.com | -| Estonia | Vaata Maailma SA | Kristi Kivilo | http://www.vaatamaailma.ee/
kristi@vaatamaailma.ee | -| Georgia | Educare Georgia | George Jibladze
Razmik Badalyan | http://www.educaregeorgia.org/
gjibladze@educaregeorgia.com
rbadalyan@educaregeorgia.com | +| Estonia | Vaata Maailma SA | Kristi Kivilo | http://www.vaatamaailma.ee
kristi@vaatamaailma.ee | +| Georgia | Educare Georgia | George Jibladze
Razmik Badalyan | http://www.educaregeorgia.org
gjibladze@educaregeorgia.com
rbadalyan@educaregeorgia.com | | Ghana | Ghana Code Club | Ernestina Edem Appiah | www.ghanacodeclub.org
tinasupport@gmail.com | | Hong Kong | Idea Maker HK | Henry Lee | www.ideamakerhk.com
henrylee@i3dprinter.com.hk | | Hong Kong | IAMAI Academy (Hong Kong) Ltd | Johnson Chu | www.iamai.hk
johnson.chu@iamai.hk | | India | CodeTigers | Sandeep Sheokand | sandeep.sheokand@gmail.com | -| India | Mozilla Indore | Mrinal Jain | http://www.mozillaindore.org/
jain.mrinal140@gmail.com | +| India | Mozilla Indore | Mrinal Jain | http://www.mozillaindore.org
jain.mrinal140@gmail.com | | India | RIshs International School | | www.rishsinternationalschool.com
principal@rishsinternationalschool.com | | India | Be Cre8v | | www.becre8v.com
sandeep@becre8v.com | | Ireland | Cybersmarties.com | Diarmuid Hudner | www.cybersmarties.com
diarmuid@cybersmarties.com | @@ -61,40 +61,41 @@ You, too, can play a leading role to get more people in your country involved! I | Japan | 特定非営利活動法人みんなのコード (Code for Everyone) | 利根川 裕太 (Yuta Tonegawa) | info@code.or.jp | | Japan | 湘南ゼミナール (Shonan Seminar) | Team Hour of Code | https://shozemi.com | | Kazakhstan | Love to Code | Alyona Tkachenko | alyona@lovetocode.kz | -| Kenya | Uzima Aid | Nicholas Juma | http://www.uzimaid.org/
nicholasjuma@uzimaid.org | -| Malaysia | Malaysia Digital Economy Corporation (MDEC) | Mohamad Zulfadli Mohd Amin | https://www.mdec.my/
zulfadli.amin@mdec.com.my | +| Kenya | Uzima Aid | Nicholas Juma | http://www.uzimaid.org
nicholasjuma@uzimaid.org | +| Malaysia | Malaysia Digital Economy Corporation (MDEC) | Mohamad Zulfadli Mohd Amin | https://www.mdec.my
zulfadli.amin@mdec.com.my | | Malaysia | Telebort Technologies Sdn Bhd | | www.telebort.com | | Mexico | Cuantrix - Fundación Televisa | Nomara Parra | www.cuantrix.mx
cuantrix@fundaciontelevisa.org | | Morocco | S.T.E.M ROBOTICS AGADIR | Dr. Isha Daramy | www.stemagadir.com
Stemagadir@gmail.com | | New Zealand | Ara Institute of Canterbury | Amitrajit Sarkar | www.ara.ac.nz
sarkara@cpit.ac.nz | | New Zealand | Google | Sally-Ann Williams | https://www.google.com/edu/cs | +| New Zealand | OMGTech! | Vivian Chandra | https://omgtech.co.nz
viv@omgtech.co.nz | Nigeria | CODE4KT- coding for kids & teens | Mayokun Odusote | www.code4kt.com
odusotemayokun@gmail.com | -| Nigeria | Dev's District Nigeria | Emmanuel Odunlade | http://devsdistrictnigeria.com/
E.odunlade@devsdistrictnigeria.com | +| Nigeria | Dev's District Nigeria | Emmanuel Odunlade | http://devsdistrictnigeria.com
E.odunlade@devsdistrictnigeria.com | | Nigeria | Odyssey Educational Foundation | Stella Uzochukwu-Denis | www.odysseyedufoundation.org
ifywayne@gmail.com | | Nigeria | KodeHauz | Glory Agatevure | http://www.kodehauz.com | | Nigeria | The Zariah Elizabeth Foundation | Akindayo Akindolani | www.tzefoundation.org
dayo@tzefoundation.org | | Norway | Lær Kidsa Koding | Line Moseng | https://kidsakoder.no | | Oman | Engineering Village | | www.ev-center.com | -| Pakistan | KnowledgeJump | Nasir Farhat Khan | http://www.knowledgejump.net/
nasir@knowledgejump.net | +| Pakistan | KnowledgeJump | Nasir Farhat Khan | http://www.knowledgejump.net
nasir@knowledgejump.net | | Pakistan | TechValley | Dr. Waqar S. Qureshi | | -| Peru | Code en mi Cole | Renzo Sousa | http://codenmicole.com/ | -| Philippines | The Startup Deck Balanga | Mark Colentava | www.techinbataan.com
mark@colentava.com | +| Peru | Code en mi Cole | Renzo Sousa | http://codenmicole.com | +| Philippines | The Startup Deck Balanga | Mark Colentava | http://www.startupbataan.com
mark@colentava.com | | Poland | UMK Toruń | Maciej M. Sysło | http://godzinakodowania.pl
syslo@ii.uni.wroc.pl | -| Portugal | Colégio EFANOR | | http://www.colegioefanor.pt/pt/ | +| Portugal | Colégio EFANOR | | http://www.colegioefanor.pt/pt | | Portugal | Gabinete de Modernização das Tecnologias Educativas | Rodolfo Pinto | http://www02.madeira-edu.pt/dre/main.aspx | | Romania | ADFABER | Alin Chiriac | alin@adfaber.org | -| Singapore | Infocomm Media Development Authority of Singapore | | https://www.imda.gov.sg/ | -| South Korea | Korea Information Science Education Federation | | http://kcode.kr/
hwankim92@gmail.com | +| Singapore | Infocomm Media Development Authority of Singapore | | https://www.imda.gov.sg | +| South Korea | Korea Information Science Education Federation | | http://kcode.kr
hwankim92@gmail.com | | Spain | Acludi | Juana Martínez | www.acludi.blogspot.com
acludi@yahoo.com | | Spain | CSTA Spain | Santiago Fernández-Cabaleiro | www.aapri.es
santiagocabaleiro@gmail.com | | Spain | Informatics Faculty - University of the Basque Country (UPV/EHU) | Edurne Larraza Mendiluze | https://www.ehu.eus/en/web/informatika-fakultatea/home
edurne.larraza@ehu.eus | | Sri Lanka | XiCigny | Dileepa Rajapaksa | dileepa@xicigny.com | | Sweden | Edvira | | http://edvira.com
hoc@edvira.com | -| Taiwan | Girls in Tech Taiwan | Jane Shih | https://facebook.com/girlsintechtaiwan/
Jane.shih@girlsintech.org | +| Taiwan | Girls in Tech Taiwan | Jane Shih | https://facebook.com/girlsintechtaiwan
Jane.shih@girlsintech.org | | Taiwan | Women Who Code Taipei | Jane Shih | https://www.womenwhocode.com/taipei
Janeshih@womenwhocode.com | | Thailand | Aksorn Education PLC | Weena Naowaprateep | www.aksorn.com
weena@aksorn.com | | Turkey | RobinCode | Gözde Erbaz | www.robincode.org
gozde@robincode.org | -| UK | Computing At School (CAS) | Abi Edwards | http://www.computingatschool.org.uk/
noe@bcs.uk | +| UK | Computing At School (CAS) | Abi Edwards | http://www.computingatschool.org.uk
noe@bcs.uk | | Vietnam | Microsoft Vietnam | Nhi Le | https://www.microsoft.com/vi-vn
nhile@microsoft.com | <%= view :signup_button %>