Skip to content

Commit

Permalink
Merge pull request #10 from feugy/season-2019
Browse files Browse the repository at this point in the history
Season 2019
  • Loading branch information
feugy authored Aug 26, 2018
2 parents 23ff8c1 + 267c5b8 commit 4b0a753
Show file tree
Hide file tree
Showing 43 changed files with 5,089 additions and 287 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: node_js
node_js:
- 7
- 8
before_install:
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ To publish a new one:
- Invoice & Lesson conflicts
- unit test for indexedDB operator support ($and, $lt, $gt, $lte, $gte)
- Mandatory fields that cannot be bypassed
- Replace mocha with jest
- Get the [coffee-coverage PR](https://github.com/benbria/coffee-coverage/pull/87) merged.
Until that don't forget to manually compile coffee to js after dependency updates:
> cd node_moduldes/coffee-coverage
> npm run build
## TODO Electron

Expand All @@ -38,6 +43,7 @@ To publish a new one:
## TODO MongoDB

- import > callstack when merging with distant mongo (works fine locally)
- update to [driver version 3](https://github.com/mongodb/node-mongodb-native/blob/HEAD/CHANGES_3.0.0.md)

[logo]: https://github.com/feugy/dancerm/raw/master/app/style/img/dancerm.png
[ci-badge]: https://travis-ci.org/feugy/dancerm.svg?branch=master
Expand Down
5 changes: 3 additions & 2 deletions app/src/controller/invoice.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ isPrintCtx = global.isPrintCtx or false
i18n = require "../#{if isPrintCtx then 'script/' else ''}labels/common"
Invoice = require "../#{if isPrintCtx then 'script/' else ''}model/invoice"
InvoiceItem = require "../#{if isPrintCtx then 'script/' else ''}model/invoice_item"
PriceList = require "../#{if isPrintCtx then 'script/' else ''}model/price_list"
{invoiceRefExtract} = require "../#{if isPrintCtx then 'script/' else ''}util/common"

# Simple validation function that check if a given value is defined and acceptable
Expand Down Expand Up @@ -326,7 +327,7 @@ class InvoiceController
@invoice.changeDate @_previous.date
@dateOpts.value = @invoice.date.valueOf()
@_previous = @invoice.toJSON()
@priceList = @i18n.priceList[@invoice.season] or @i18n.priceList.default
PriceList.findForSeason @invoice.season, (err, list) => @priceList = list or []
@_setChanged false

# **private**
Expand All @@ -335,7 +336,7 @@ class InvoiceController
_onLoad: (invoice) =>
@invoice = invoice
@teacher = @conf.teachers[@invoice.selectedTeacher]
@priceList = @i18n.priceList[@invoice.season] or @i18n.priceList.default
PriceList.findForSeason @invoice.season, (err, list) => @priceList = list or []
@dueDate = @invoice.dueDate
@isReadOnly = @invoice.sent?
@dateOpts.value = @invoice.date.valueOf()
Expand Down
27 changes: 27 additions & 0 deletions app/src/controller/lessons.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,33 @@ module.exports = class LessonsController
@_setChanged false
@scope.$apply() unless @scope.$$phase

# When a given lesson is moved on planning, change its hour and date
#
# @param course [Object] moved course
# @param day [String] new day for this course
# @param hour [String] new hour for this course
# @param minutes [String] new minutes for this course
# @returns [Boolean] true to complete the move in planning directive, false to cancel it
moveLesson: (lesson, day, hour, minutes) =>
if not lesson? or lesson.invoiceId?
@dialog.messageBox(@i18n.ttl.invalidOperation, @i18n.msg.readOnlyLesson, [
{label: @i18n.btn.ok}
]
)
return false
# update date (duration will not be modified) and save
lesson.date = @startDay.clone().add(toDayOffset(day), 'd').hours(+hour).minutes(+minutes).seconds(0).milliseconds(0)
lesson.save (err) =>
if err?
console.error err
return @dialog.messageBox(@i18n.ttl.saveError, err.message, [
{label: @i18n.btn.ok}
]
).result.then done
console.log "Moved lesson #{lesson.id} to #{lesson.date}"
@scope.$apply() unless @scope.$$phase
true

# Display current week title
# @returns [String] current week title with from and to dates
currentWeek: =>
Expand Down
171 changes: 170 additions & 1 deletion app/src/controller/settings.coffee
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
_ = require 'lodash'
{dialog} = require('electron').remote
i18n = require '../labels/common'
DanceClass = require '../model/dance_class'
ConflictsController = require './conflicts'
{buildStyles, getColorsFromTheme} = require '../util/common'
{buildStyles, getColorsFromTheme, extractDateDetails} = require '../util/common'
{version} = require '../../../package.json'

# Edit application settings
Expand Down Expand Up @@ -44,6 +45,18 @@ module.exports = class SettingsController
# list of available theme
themes: []

# List of available seasons
seasons: []

# currently edited season
currentSeason: null

# planning for currently edited season
planning: null

# currently edited (or added) dance slass in current planning
editedCourse: null

# **private**
# flag to temporary disable button while theme are building
_building: false
Expand All @@ -64,6 +77,7 @@ module.exports = class SettingsController
@conf.load () =>
@vat = @conf.vat * 100
@scope.$apply()
@planningDays = i18n.planning.days[...6]
@themes = (label: i18n.themes[name], value: name for name of i18n.themes)
@_building = false
@_dumpDialogDisplayed = false
Expand All @@ -86,6 +100,18 @@ module.exports = class SettingsController
@filter('i18n') 'lbl.version', args: version: '3.3.7'
]}
]
@seasons = []
@editedCourse = null
@halls = ['Gratte-ciel 1', 'Gratte-ciel 2', 'Croix-Luizet']

@rootScope.$on 'model-initialized', initPlannings = =>
DanceClass.listSeasons (err, seasons) =>
return console.error err if err?
@seasons = seasons
@onSelectSeason @seasons[0] unless @seasons.length is 0
@rootScope.$apply()
@rootScope.$on 'model-imported', initPlannings
initPlannings()

# cancel navigation if asking for location
if location.search()?.firstRun
Expand Down Expand Up @@ -135,6 +161,149 @@ module.exports = class SettingsController
@conf.teachers.splice idx, 1
@onChangeTeachers()

# When a season is selected, shows its planning
#
# @param season [String] selected season
onSelectSeason: (season) =>
@currentSeason = season
DanceClass.getPlanning season, (err, planning) =>
return console.error err if err?
@planning = planning
@onSelectCourse null

# Add a new season to the season list, and loads it
onNewSeason: =>
# compute next season
[year] = @seasons[0].split '/'
@seasons.unshift "#{+year+1}/#{+year+2}"
@onSelectSeason @seasons[0]

# When a dance class is selected in the planning, update edition form.
# Discard pending changes on the previsouly edited course.
#
# @param course [Object] selected dance class
onSelectCourse: (course) =>
# Restore initial values. Will be a noop if saved
@onRestoreCourse @editedCourse
@editedCourse = course
@rootScope.$apply()

# Detect changes on currently edited course
#
# @param field [String] modified field
# @param value [*] for some field, the new selected value
onCourseChanged: (field, value) =>
return unless @editedCourse
if 'hall' is field
@editedCourse.hall = value
else if 'kind' is field
# search for similar kind
similar = @planning.find ({kind, id}) => kind is @editedCourse.kind && id isnt @editedCourse.id
@editedCourse.color = (
if similar?
similar.color
else
# compute first unused color
usedColors = @planning.reduce (colors, {color}) =>
num = +color.replace 'color', ''
colors.push num unless colors.includes num
colors
, []
last = _.sortBy(usedColors).pop()
"color#{if last? then last + 1 else 1}"
)

# When a given course is moved on planning, change its hour and date
#
# @param course [Object] moved course
# @param day [String] new day for this course
# @param hour [String] new hour for this course
# @param minutes [String] new minutes for this course
# @returns [Boolean] to complete the move in planning directive
onDanceClassMoved: (course, day, hour, minutes) =>
duration = course.duration
# change start date, and updates the end by setting duration
course.start = "#{day} #{hour}:#{minutes}#{if minutes is 0 then '0' else ''}"
course.duration = duration
console.log "Moved course", course, "to #{day} #{hour}:#{minutes}"
@onSaveCourse course
true

# Create a new (unsaved) course for that season
#
# @param day [String] selected day, ie. Tue
# @param hour [String] selected hour and minutes, ie. 15:30
onCreateCourse: (day, hour) =>
created = new DanceClass
season: @currentSeason
start: "#{day} #{hour}"
hall: @halls[0]
created.duration = 60
@onSelectCourse created

# Save a given course to persist changes
#
# @param course [Object] saved course
onSaveCourse: (course) =>
return unless course?
course.save () =>
return console.error err if err?
console.log "Course saved", course
# reload the full season for newly created courses
@onSelectSeason @currentSeason

# Restore the given course to its previous values
#
# @param course [Object] selected dance class
onRestoreCourse: (course) =>
course.restore() if course?

# Show modal confirmation, then remove that course.
# Refresh planning after deletion, and reset edited course if applicable
#
# @param course [Object] removed course
onRemoveCourse: (course) =>
return unless course?
doRemove = =>
# remove that course
course.remove (err) =>
return console.error err if err?
@editedCourse = null if course is @editedCourse
# reset planning
@onSelectSeason @currentSeason

# get the number of registrations
course.getDancers (err, dancers = []) =>
if 0 is dancers.length
# removes immediately if there's no dancers
doRemove()
else
@dialog.messageBox(i18n.ttl.confirm, @filter('i18n')('msg.removeDanceClass', args: {
course...
dancers: dancers.length
start: @formatCourseStart course
}) , [
{label: i18n.btn.no, cssClass: 'btn-warning'}
{label: i18n.btn.yes, result: true}
]
).result.then (confirmed) => doRemove() if confirmed

# Detect changes on the edited course
#
# @returns [Boolean] true if there's an edited course, and it has pending changes
hasEditedCourseChanged: =>
return false unless @editedCourse
not _.isEqual @editedCourse.toJSON(), @editedCourse._raw

# Format course start hour for friendly users :)
#
# @param course [Object] the formated course
# @return [String] the formated start day and time
formatCourseStart: (course) =>
return unless course
{day, hour, minutes} = extractDateDetails course.start
"#{i18n.lbl[day]} #{hour}h#{if minutes > 0 then minutes else '00'}"

# According to the selected theme, rebuild styles and apply them.
# New theme is saved into configuration, and button is temporary disabled while compiling
#
Expand Down
8 changes: 5 additions & 3 deletions app/src/directive/dancer.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Dancer = require '../model/dancer'
class DancerDirective

# Controller dependencies
@$inject: ['$q']
@$inject: ['$scope', '$q']

# Labels for rendering
i18n: i18n
Expand All @@ -31,8 +31,9 @@ class DancerDirective

# Controller constructor: bind methods and attributes to current scope
#
# @param scope [Object] directive's scope
# @param q [Object] Angular's promise factory
constructor: (@q) ->
constructor: (@scope, @q) ->
@_reqInProgress = false

@birthOpts =
Expand All @@ -44,7 +45,8 @@ class DancerDirective

# reset birth date to dancer's one
@birthOpts.open = false
@birthOpts.value = if @src?.birth?.isValid() then @src.birth.valueOf() else null
@scope.$watch 'ctrl.src.birth', =>
@birthOpts.value = if @src?.birth?.isValid() then @src.birth.valueOf() else null

# Invoked by view to update dancer's title according to selected item
#
Expand Down
12 changes: 6 additions & 6 deletions app/src/directive/invoice_item.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ module.exports = (app) ->
<span data-ng-if="!ctrl.readOnly" class="input-group" data-uib-dropdown keyboard-nav>
<textarea data-auto-height name="name" data-ng-model="ctrl.src.name" data-ng-class="ctrl.isRequired('name')" data-set-null></textarea>
<a href="" class="input-group-addon" uib-dropdown-toggle><i class="glyphicon glyphicon-triangle-bottom"></i></a>
<ul class="dropdown-menu">
<li data-ng-repeat="option in ctrl.options">
<a href="" data-ng-if="!option.category" data-ng-click="ctrl.prefill(option)">{{option.label || option.name}}</a>
<span data-ng-if="option.category" class="category">{{option.category}}</span>
<ul class="dropdown-menu price-list">
<li data-ng-repeat="price in ctrl.priceList.flatList()">
<a href="" data-ng-if="!price.category" data-ng-click="ctrl.prefill(price)">{{price.label || price.name}}</a>
<span data-ng-if="price.category" class="category">{{price.category}}</span>
</li>
</ul>
</span>
Expand Down Expand Up @@ -106,8 +106,8 @@ module.exports = (app) ->
src: '='
# array of missing fields
requiredFields: '='
# different options to prefill src. The 'label' attribute might be used as option content
options: '=?'
# the price list used to prefill src.
priceList: '=?'
# read-only flag.
readOnly: '=?'
# Vat column flag.
Expand Down
8 changes: 3 additions & 5 deletions app/src/directive/lesson_list.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ class LessonListDirective
@groups = []

# free listeners
@scope.$on '$destroy', =>
unwatch?()

@_onMakeGroups()
@scope.$on '$destroy', => unwatch?()
setTimeout @_onMakeGroups, 0

# When a given model is selected or unselected, update the global selected array
#
Expand Down Expand Up @@ -89,7 +87,7 @@ class LessonListDirective
return console.error err if err?
@groups.sort ({dancer: dancerA}, {dancer: dancerB}) ->
if dancerA.lastname.toLowerCase() < dancerB.lastname.toLowerCase() then -1 else 1
@scope.$apply()
@scope.$apply() unless @scope.$$phase

# The tags directive displays tags relative at search criteria
module.exports = (app) ->
Expand Down
Loading

0 comments on commit 4b0a753

Please sign in to comment.