funifier-mcp 0.2.0 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.cursor/rules/funifier.mdc +91 -0
  2. package/.github/copilot-instructions.md +83 -0
  3. package/AGENTS.md +97 -0
  4. package/README.md +247 -78
  5. package/datasource-funifier-docs/knowledge/guides/aggregates.md +152 -152
  6. package/datasource-funifier-docs/knowledge/guides/database-access.md +132 -132
  7. package/datasource-funifier-docs/knowledge/guides/java-entities.md +373 -373
  8. package/datasource-funifier-docs/knowledge/guides/java-libraries.md +330 -330
  9. package/datasource-funifier-docs/knowledge/guides/java-managers.md +509 -509
  10. package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +271 -271
  11. package/datasource-funifier-docs/knowledge/index.md +121 -121
  12. package/datasource-funifier-docs/knowledge/modules/achievement.md +46 -46
  13. package/datasource-funifier-docs/knowledge/modules/action-log.md +88 -88
  14. package/datasource-funifier-docs/knowledge/modules/action.md +80 -80
  15. package/datasource-funifier-docs/knowledge/modules/auth.md +104 -104
  16. package/datasource-funifier-docs/knowledge/modules/avatar.md +28 -28
  17. package/datasource-funifier-docs/knowledge/modules/backup.md +40 -40
  18. package/datasource-funifier-docs/knowledge/modules/challenge.md +91 -91
  19. package/datasource-funifier-docs/knowledge/modules/compact.md +40 -40
  20. package/datasource-funifier-docs/knowledge/modules/competition.md +149 -149
  21. package/datasource-funifier-docs/knowledge/modules/crossword.md +41 -41
  22. package/datasource-funifier-docs/knowledge/modules/csv-data.md +30 -30
  23. package/datasource-funifier-docs/knowledge/modules/custom-object.md +53 -53
  24. package/datasource-funifier-docs/knowledge/modules/database.md +241 -241
  25. package/datasource-funifier-docs/knowledge/modules/folder.md +111 -111
  26. package/datasource-funifier-docs/knowledge/modules/kpi-formulas.md +23 -23
  27. package/datasource-funifier-docs/knowledge/modules/lastmile.md +45 -45
  28. package/datasource-funifier-docs/knowledge/modules/leaderboard.md +98 -98
  29. package/datasource-funifier-docs/knowledge/modules/level.md +83 -83
  30. package/datasource-funifier-docs/knowledge/modules/lottery.md +112 -112
  31. package/datasource-funifier-docs/knowledge/modules/marketplace.md +27 -27
  32. package/datasource-funifier-docs/knowledge/modules/mystery.md +82 -82
  33. package/datasource-funifier-docs/knowledge/modules/notification.md +40 -40
  34. package/datasource-funifier-docs/knowledge/modules/patterns.md +1096 -1096
  35. package/datasource-funifier-docs/knowledge/modules/player.md +101 -101
  36. package/datasource-funifier-docs/knowledge/modules/point.md +67 -67
  37. package/datasource-funifier-docs/knowledge/modules/public.md +253 -253
  38. package/datasource-funifier-docs/knowledge/modules/question.md +136 -136
  39. package/datasource-funifier-docs/knowledge/modules/quiz.md +163 -163
  40. package/datasource-funifier-docs/knowledge/modules/scheduler.md +58 -58
  41. package/datasource-funifier-docs/knowledge/modules/security.md +169 -169
  42. package/datasource-funifier-docs/knowledge/modules/staging.md +28 -28
  43. package/datasource-funifier-docs/knowledge/modules/static-repo.md +41 -41
  44. package/datasource-funifier-docs/knowledge/modules/story.md +42 -42
  45. package/datasource-funifier-docs/knowledge/modules/studio-page.md +180 -180
  46. package/datasource-funifier-docs/knowledge/modules/swap.md +132 -132
  47. package/datasource-funifier-docs/knowledge/modules/team.md +75 -75
  48. package/datasource-funifier-docs/knowledge/modules/trigger.md +189 -189
  49. package/datasource-funifier-docs/knowledge/modules/upload.md +155 -155
  50. package/datasource-funifier-docs/knowledge/modules/virtual-good.md +99 -99
  51. package/datasource-funifier-docs/knowledge/modules/webhook.md +41 -41
  52. package/datasource-funifier-docs/knowledge/modules/websocket.md +41 -41
  53. package/datasource-funifier-docs/knowledge/modules/widget.md +42 -42
  54. package/datasource-funifier-docs/process-gtm-saas.md +143 -143
  55. package/datasource-funifier-docs/process-instagram.md +88 -88
  56. package/datasource-funifier-docs/process.md +1826 -1826
  57. package/datasource-funifier-docs/readme.md +132 -132
  58. package/dist/cli/config-writers.js +11 -11
  59. package/dist/mcp/bundle.js +82 -77
  60. package/package.json +70 -67
  61. package/skills/funifier-create-aggregate/SKILL.md +126 -126
  62. package/skills/funifier-create-custom-page/SKILL.md +126 -126
  63. package/skills/funifier-create-scheduler/SKILL.md +126 -126
  64. package/skills/funifier-create-trigger/SKILL.md +127 -127
@@ -1,42 +1,42 @@
1
- # Story (História)
2
-
3
- **Acesso Studio:** `/studio/story`
4
- **API Endpoint:** `/v3/story`
5
-
6
- ## O que é
7
-
8
- Criação de histórias interativas com múltiplos caminhos e finais. Permite criar narrativas gamificadas nas quais o jogador faz escolhas que afetam o desenrolar da trama. Cenários, personagens, imagens e vídeos podem ser utilizados para enriquecer a experiência.
9
-
10
- ## Quando usar
11
-
12
- - Para treinamentos baseados em storytelling
13
- - Para campanhas de marketing com desfechos alternativos
14
- - Para jogos educacionais interativos
15
- - Para experiências de role-playing
16
-
17
- ## Checklist de Configuração no Studio
18
-
19
- - [ ] Definir título e descrição da história
20
- - [ ] Criar cenários/cenas com textos e mídias
21
- - [ ] Definir ramificações e escolhas
22
- - [ ] Configurar finais alternativos
23
-
24
- ## API Endpoints
25
-
26
- ### Listar Stories
27
- **Método:** GET
28
- **Endpoint:** `/v3/story`
29
-
30
- ### Criar Story
31
- **Método:** POST
32
- **Endpoint:** `/v3/story`
33
-
34
- ### Deletar Story
35
- **Método:** DELETE
36
- **Endpoint:** `/v3/story/:id`
37
-
38
- ## Validações e Testes
39
-
40
- - [ ] História aparece na lista
41
- - [ ] Navegação entre cenários funciona
42
- - [ ] Escolhas do jogador são registradas
1
+ # Story (História)
2
+
3
+ **Acesso Studio:** `/studio/story`
4
+ **API Endpoint:** `/v3/story`
5
+
6
+ ## O que é
7
+
8
+ Criação de histórias interativas com múltiplos caminhos e finais. Permite criar narrativas gamificadas nas quais o jogador faz escolhas que afetam o desenrolar da trama. Cenários, personagens, imagens e vídeos podem ser utilizados para enriquecer a experiência.
9
+
10
+ ## Quando usar
11
+
12
+ - Para treinamentos baseados em storytelling
13
+ - Para campanhas de marketing com desfechos alternativos
14
+ - Para jogos educacionais interativos
15
+ - Para experiências de role-playing
16
+
17
+ ## Checklist de Configuração no Studio
18
+
19
+ - [ ] Definir título e descrição da história
20
+ - [ ] Criar cenários/cenas com textos e mídias
21
+ - [ ] Definir ramificações e escolhas
22
+ - [ ] Configurar finais alternativos
23
+
24
+ ## API Endpoints
25
+
26
+ ### Listar Stories
27
+ **Método:** GET
28
+ **Endpoint:** `/v3/story`
29
+
30
+ ### Criar Story
31
+ **Método:** POST
32
+ **Endpoint:** `/v3/story`
33
+
34
+ ### Deletar Story
35
+ **Método:** DELETE
36
+ **Endpoint:** `/v3/story/:id`
37
+
38
+ ## Validações e Testes
39
+
40
+ - [ ] História aparece na lista
41
+ - [ ] Navegação entre cenários funciona
42
+ - [ ] Escolhas do jogador são registradas
@@ -1,180 +1,180 @@
1
- # Studio Page (Página Customizada do Studio)
2
-
3
- **Acesso Studio:** `/studio/page`
4
-
5
- ## O que é
6
-
7
- Páginas customizadas dentro do Funifier Studio para atender necessidades específicas dos administradores. Permitem criar dashboards, CRUDs, gráficos, relatórios — tudo com AngularJS + Bootstrap.
8
-
9
- ## Configuração
10
-
11
- Cada página é um JSON com:
12
- - `_id` — Identificador único (auto-gerado se omitido)
13
- - `display` — `true` para aparecer no menu do Studio, `false` para páginas internas
14
- - `title` — Nome exibido no menu
15
- - `slug` — Caminho relativo (ex: `studio/custom/hello`). Suporta parâmetros: `studio/custom/car/form/:id`
16
- - `html` — Código HTML (usa Bootstrap CSS)
17
- - `script` — Código JavaScript (roda no contexto AngularJS)
18
-
19
- ## Contexto JavaScript
20
-
21
- Dentro do `script`, você tem acesso a:
22
- - `$scope` — Scope do AngularJS
23
- - `$http` — Para requisições HTTP
24
- - `$location` — Para navegação entre páginas (`$location.path("/studio/custom/...")`)
25
- - `$routeParams` — Para ler parâmetros do slug (ex: `$routeParams.id`)
26
- - `Marketplace` — Objeto utilitário (ver abaixo)
27
-
28
- ### Marketplace API
29
-
30
- | Método | Descrição | Exemplo de retorno |
31
- |--------|-----------|-------------------|
32
- | `Marketplace.auth.getService()` | URL da API Funifier | `https://service2.funifier.com` |
33
- | `Marketplace.auth.getAuthorization()` | Token de acesso do usuário logado no Studio | Bearer token |
34
- | `Marketplace.range.parse(content_range)` | Analisa header `content-range` para paginação | `{page, pages, count, ...}` |
35
- | `Marketplace.range.paginate(page, content_range)` | Gera header `Range` para ir a uma página | `items=0-100` |
36
-
37
- ## Diretivas Disponíveis
38
-
39
- - `<image-picker>` — Seletor de imagem (URL, local, galeria). Atributos: `on-change`, `show-picker-url`, `show-picker-local`, `show-picker-gallery`, `upload-max-size`, `transform`
40
- - `<principal-picker>` — Seletor de jogadores/equipes. Atributos: `title`, `model`, `show-picker-player`, `black-list`, `max`
41
- - `<input-extra>` — Campos extras dinâmicos. Atributos: `title`, `show-inline`, `model`
42
-
43
- ## Bibliotecas Disponíveis
44
-
45
- - **Highcharts** — Gráficos (pizza, barra, linhas, etc.)
46
- - **Bootstrap** — CSS framework (classes `btn`, `table`, `form-control`, etc.)
47
- - **Glyphicons** — Ícones via `glyphicon glyphicon-*`
48
- - Traduções: `{{'SAVE'|translate}}`, `{{'CANCEL'|translate}}`, `{{'BACK'|translate}}`, `{{'NEW'|translate}}`, `{{'SEARCH_FOR'|translate}}`
49
-
50
- ## Exemplos
51
-
52
- ### 1. Hello World
53
- ```json
54
- {
55
- "_id": "hello1",
56
- "display": true,
57
- "title": "Hello World!",
58
- "slug": "studio/custom/hello",
59
- "html": "<button ng-click=\"hello()\" class=\"btn btn-warning\">Hello</button>",
60
- "script": "$scope.hello = function(){ alert('Hello World!'); }"
61
- }
62
- ```
63
-
64
- ### 2. Lista Simples (Desafios)
65
- ```javascript
66
- /* "display": true, "title": "Quest List", "slug": "studio/custom/challenge" */
67
- $scope.all = [];
68
- $scope.list = function () {
69
- $http({method: 'GET', url:Marketplace.auth.getService() + '/v3/database/challenge',
70
- headers: {"Authorization": Marketplace.auth.getAuthorization(), "content-type": "application/json"}
71
- }).then(function(data){
72
- $scope.all = data.data;
73
- });
74
- };
75
- $scope.list();
76
- ```
77
-
78
- ### 3. Lista Paginada (Players)
79
- ```javascript
80
- /* "display": true, "title": "Player List", "slug": "studio/custom/player" */
81
- $scope.range = { request: "items=0-100" };
82
- $scope.gotopage = function(page){
83
- $scope.range.request = Marketplace.range.paginate(page, $scope.range.response);
84
- $scope.list();
85
- };
86
- $scope.paginate = function (total) {
87
- var to = $scope.range.page + total;
88
- $scope.range.request = Marketplace.range.paginate(to, $scope.range.response);
89
- $scope.list();
90
- };
91
- $scope.all = [];
92
- $scope.list = function () {
93
- $http({method: 'GET', url:Marketplace.auth.getService() + '/v3/database/player',
94
- headers: {"Authorization": Marketplace.auth.getAuthorization(), "Range": $scope.range.request, "content-type": "application/json"}
95
- }).then(function(data){
96
- $scope.all = data.data;
97
- $scope.range = Marketplace.range.parse(data.headers(["content-range"]));
98
- });
99
- };
100
- $scope.list();
101
- ```
102
- HTML de paginação:
103
- ```html
104
- <div>
105
- Page <input type="number" ng-model="range.page" ng-change="gotopage(range.page)" style="width:40px;" /> of {{range.pages}}
106
- <button ng-click="paginate(-1)" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span></button>
107
- <button ng-click="paginate(+1)" class="btn btn-default"><span class="glyphicon glyphicon-chevron-right"></span></button>
108
- </div>
109
- ```
110
-
111
- ### 4. CRUD Completo (Carros)
112
- **Lista** (`display: true`, slug: `studio/custom/car/list`): Lista + busca + paginação + botões editar/excluir + botão "NEW"
113
- - DELETE: `$http({method: 'DELETE', url: API + "/v3/database/car__c?q=_id:'" + id + "'", ...})`
114
- - Navegação: `$location.path("/studio/custom/car/form/" + id)` ou `"/studio/custom/car/form/new"`
115
-
116
- **Formulário** (`display: false`, slug: `studio/custom/car/form/:id`):
117
- - Carrega via GET: `/v3/database/car__c?strict=true&q=_id:'ID'`
118
- - Salva via PUT: `/v3/database/car__c`
119
- - Usa `$routeParams.id` para saber se é edição ou criação (`"new"` = novo)
120
- - `<image-picker on-change="setImage" ...>` para upload de imagem
121
-
122
- ### 5. Gráfico Highcharts (Active Players)
123
- ```javascript
124
- /* "display": true, "title": "Daily Active Players", "slug": "studio/custom/kpi/players" */
125
- $scope.load = function () {
126
- $http({
127
- method: 'POST', url: Marketplace.auth.getService() + '/v3/database/action_log/aggregate',
128
- headers: { Authorization: Marketplace.auth.getAuthorization(), 'content-type': 'application/json'},
129
- data: [
130
- {"$match": { "time": { "$gte": { "$date": "-1y" } } } },
131
- {"$addFields": { "day": { "$dayOfYear": "$time" } } },
132
- {"$group": { "_id": { "player": "$userId", "day": "$day" }, "time": { "$max": "$time" }}},
133
- {"$group": { "_id": "$_id.day", "total_users": { "$sum": 1 }, "time": { "$max": "$time" }}},
134
- {"$project": { "_id": 0, "day_of_year": '$_id', "total": '$total_users', "time": 1}},
135
- {"$sort": { "day_of_year": 1 } }
136
- ]
137
- }).then(function (data) {
138
- var transformedData = data.data.map(function (d) { return [d.time, d.total]; });
139
- new Highcharts.Chart({
140
- chart: { renderTo: 'chart', type: 'line' },
141
- title: { text: 'Daily Active Players' },
142
- xAxis: { type: 'datetime' },
143
- yAxis: { title: { text: 'Total' } },
144
- series: [{ name: 'Total over time', data: transformedData }],
145
- credits: { enabled: false }
146
- });
147
- });
148
- };
149
- $scope.load();
150
- ```
151
-
152
- ## API REST para Gerenciar Páginas
153
-
154
- **IMPORTANTE:** As páginas customizadas ficam na collection `studio_page` (NÃO `page`).
155
-
156
- ```bash
157
- # Criar/atualizar página
158
- curl -X PUT "https://service2.funifier.com/v3/database/studio_page" \
159
- -H "Authorization: Bearer TOKEN" \
160
- -H "Content-Type: application/json" \
161
- -d '{"_id":"minha_pagina", "display":true, "title":"Minha Página", "slug":"studio/custom/minha-pagina", "html":"<h1>Oi</h1>", "script":"/* js */"}'
162
-
163
- # Listar páginas
164
- curl "https://service2.funifier.com/v3/database/studio_page" \
165
- -H "Authorization: Bearer TOKEN"
166
-
167
- # Deletar página
168
- curl -X DELETE "https://service2.funifier.com/v3/database/studio_page?q=_id:'ID'" \
169
- -H "Authorization: Bearer TOKEN"
170
- ```
171
-
172
- ## Checklist
173
-
174
- - [ ] Definir `_id`, `title`, `slug`, `display`
175
- - [ ] Escrever `html` com Bootstrap
176
- - [ ] Escrever `script` com AngularJS
177
- - [ ] Usar `Marketplace.auth.*` para autenticação
178
- - [ ] Usar `?strict=true` em GETs para preservar tipos BSON
179
- - [ ] Testar no Studio (navegar até a URL do slug)
180
- - [ ] Para CRUDs: criar página de lista (display:true) + formulário (display:false)
1
+ # Studio Page (Página Customizada do Studio)
2
+
3
+ **Acesso Studio:** `/studio/page`
4
+
5
+ ## O que é
6
+
7
+ Páginas customizadas dentro do Funifier Studio para atender necessidades específicas dos administradores. Permitem criar dashboards, CRUDs, gráficos, relatórios — tudo com AngularJS + Bootstrap.
8
+
9
+ ## Configuração
10
+
11
+ Cada página é um JSON com:
12
+ - `_id` — Identificador único (auto-gerado se omitido)
13
+ - `display` — `true` para aparecer no menu do Studio, `false` para páginas internas
14
+ - `title` — Nome exibido no menu
15
+ - `slug` — Caminho relativo (ex: `studio/custom/hello`). Suporta parâmetros: `studio/custom/car/form/:id`
16
+ - `html` — Código HTML (usa Bootstrap CSS)
17
+ - `script` — Código JavaScript (roda no contexto AngularJS)
18
+
19
+ ## Contexto JavaScript
20
+
21
+ Dentro do `script`, você tem acesso a:
22
+ - `$scope` — Scope do AngularJS
23
+ - `$http` — Para requisições HTTP
24
+ - `$location` — Para navegação entre páginas (`$location.path("/studio/custom/...")`)
25
+ - `$routeParams` — Para ler parâmetros do slug (ex: `$routeParams.id`)
26
+ - `Marketplace` — Objeto utilitário (ver abaixo)
27
+
28
+ ### Marketplace API
29
+
30
+ | Método | Descrição | Exemplo de retorno |
31
+ |--------|-----------|-------------------|
32
+ | `Marketplace.auth.getService()` | URL da API Funifier | `https://service2.funifier.com` |
33
+ | `Marketplace.auth.getAuthorization()` | Token de acesso do usuário logado no Studio | Bearer token |
34
+ | `Marketplace.range.parse(content_range)` | Analisa header `content-range` para paginação | `{page, pages, count, ...}` |
35
+ | `Marketplace.range.paginate(page, content_range)` | Gera header `Range` para ir a uma página | `items=0-100` |
36
+
37
+ ## Diretivas Disponíveis
38
+
39
+ - `<image-picker>` — Seletor de imagem (URL, local, galeria). Atributos: `on-change`, `show-picker-url`, `show-picker-local`, `show-picker-gallery`, `upload-max-size`, `transform`
40
+ - `<principal-picker>` — Seletor de jogadores/equipes. Atributos: `title`, `model`, `show-picker-player`, `black-list`, `max`
41
+ - `<input-extra>` — Campos extras dinâmicos. Atributos: `title`, `show-inline`, `model`
42
+
43
+ ## Bibliotecas Disponíveis
44
+
45
+ - **Highcharts** — Gráficos (pizza, barra, linhas, etc.)
46
+ - **Bootstrap** — CSS framework (classes `btn`, `table`, `form-control`, etc.)
47
+ - **Glyphicons** — Ícones via `glyphicon glyphicon-*`
48
+ - Traduções: `{{'SAVE'|translate}}`, `{{'CANCEL'|translate}}`, `{{'BACK'|translate}}`, `{{'NEW'|translate}}`, `{{'SEARCH_FOR'|translate}}`
49
+
50
+ ## Exemplos
51
+
52
+ ### 1. Hello World
53
+ ```json
54
+ {
55
+ "_id": "hello1",
56
+ "display": true,
57
+ "title": "Hello World!",
58
+ "slug": "studio/custom/hello",
59
+ "html": "<button ng-click=\"hello()\" class=\"btn btn-warning\">Hello</button>",
60
+ "script": "$scope.hello = function(){ alert('Hello World!'); }"
61
+ }
62
+ ```
63
+
64
+ ### 2. Lista Simples (Desafios)
65
+ ```javascript
66
+ /* "display": true, "title": "Quest List", "slug": "studio/custom/challenge" */
67
+ $scope.all = [];
68
+ $scope.list = function () {
69
+ $http({method: 'GET', url:Marketplace.auth.getService() + '/v3/database/challenge',
70
+ headers: {"Authorization": Marketplace.auth.getAuthorization(), "content-type": "application/json"}
71
+ }).then(function(data){
72
+ $scope.all = data.data;
73
+ });
74
+ };
75
+ $scope.list();
76
+ ```
77
+
78
+ ### 3. Lista Paginada (Players)
79
+ ```javascript
80
+ /* "display": true, "title": "Player List", "slug": "studio/custom/player" */
81
+ $scope.range = { request: "items=0-100" };
82
+ $scope.gotopage = function(page){
83
+ $scope.range.request = Marketplace.range.paginate(page, $scope.range.response);
84
+ $scope.list();
85
+ };
86
+ $scope.paginate = function (total) {
87
+ var to = $scope.range.page + total;
88
+ $scope.range.request = Marketplace.range.paginate(to, $scope.range.response);
89
+ $scope.list();
90
+ };
91
+ $scope.all = [];
92
+ $scope.list = function () {
93
+ $http({method: 'GET', url:Marketplace.auth.getService() + '/v3/database/player',
94
+ headers: {"Authorization": Marketplace.auth.getAuthorization(), "Range": $scope.range.request, "content-type": "application/json"}
95
+ }).then(function(data){
96
+ $scope.all = data.data;
97
+ $scope.range = Marketplace.range.parse(data.headers(["content-range"]));
98
+ });
99
+ };
100
+ $scope.list();
101
+ ```
102
+ HTML de paginação:
103
+ ```html
104
+ <div>
105
+ Page <input type="number" ng-model="range.page" ng-change="gotopage(range.page)" style="width:40px;" /> of {{range.pages}}
106
+ <button ng-click="paginate(-1)" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span></button>
107
+ <button ng-click="paginate(+1)" class="btn btn-default"><span class="glyphicon glyphicon-chevron-right"></span></button>
108
+ </div>
109
+ ```
110
+
111
+ ### 4. CRUD Completo (Carros)
112
+ **Lista** (`display: true`, slug: `studio/custom/car/list`): Lista + busca + paginação + botões editar/excluir + botão "NEW"
113
+ - DELETE: `$http({method: 'DELETE', url: API + "/v3/database/car__c?q=_id:'" + id + "'", ...})`
114
+ - Navegação: `$location.path("/studio/custom/car/form/" + id)` ou `"/studio/custom/car/form/new"`
115
+
116
+ **Formulário** (`display: false`, slug: `studio/custom/car/form/:id`):
117
+ - Carrega via GET: `/v3/database/car__c?strict=true&q=_id:'ID'`
118
+ - Salva via PUT: `/v3/database/car__c`
119
+ - Usa `$routeParams.id` para saber se é edição ou criação (`"new"` = novo)
120
+ - `<image-picker on-change="setImage" ...>` para upload de imagem
121
+
122
+ ### 5. Gráfico Highcharts (Active Players)
123
+ ```javascript
124
+ /* "display": true, "title": "Daily Active Players", "slug": "studio/custom/kpi/players" */
125
+ $scope.load = function () {
126
+ $http({
127
+ method: 'POST', url: Marketplace.auth.getService() + '/v3/database/action_log/aggregate',
128
+ headers: { Authorization: Marketplace.auth.getAuthorization(), 'content-type': 'application/json'},
129
+ data: [
130
+ {"$match": { "time": { "$gte": { "$date": "-1y" } } } },
131
+ {"$addFields": { "day": { "$dayOfYear": "$time" } } },
132
+ {"$group": { "_id": { "player": "$userId", "day": "$day" }, "time": { "$max": "$time" }}},
133
+ {"$group": { "_id": "$_id.day", "total_users": { "$sum": 1 }, "time": { "$max": "$time" }}},
134
+ {"$project": { "_id": 0, "day_of_year": '$_id', "total": '$total_users', "time": 1}},
135
+ {"$sort": { "day_of_year": 1 } }
136
+ ]
137
+ }).then(function (data) {
138
+ var transformedData = data.data.map(function (d) { return [d.time, d.total]; });
139
+ new Highcharts.Chart({
140
+ chart: { renderTo: 'chart', type: 'line' },
141
+ title: { text: 'Daily Active Players' },
142
+ xAxis: { type: 'datetime' },
143
+ yAxis: { title: { text: 'Total' } },
144
+ series: [{ name: 'Total over time', data: transformedData }],
145
+ credits: { enabled: false }
146
+ });
147
+ });
148
+ };
149
+ $scope.load();
150
+ ```
151
+
152
+ ## API REST para Gerenciar Páginas
153
+
154
+ **IMPORTANTE:** As páginas customizadas ficam na collection `studio_page` (NÃO `page`).
155
+
156
+ ```bash
157
+ # Criar/atualizar página
158
+ curl -X PUT "https://service2.funifier.com/v3/database/studio_page" \
159
+ -H "Authorization: Bearer TOKEN" \
160
+ -H "Content-Type: application/json" \
161
+ -d '{"_id":"minha_pagina", "display":true, "title":"Minha Página", "slug":"studio/custom/minha-pagina", "html":"<h1>Oi</h1>", "script":"/* js */"}'
162
+
163
+ # Listar páginas
164
+ curl "https://service2.funifier.com/v3/database/studio_page" \
165
+ -H "Authorization: Bearer TOKEN"
166
+
167
+ # Deletar página
168
+ curl -X DELETE "https://service2.funifier.com/v3/database/studio_page?q=_id:'ID'" \
169
+ -H "Authorization: Bearer TOKEN"
170
+ ```
171
+
172
+ ## Checklist
173
+
174
+ - [ ] Definir `_id`, `title`, `slug`, `display`
175
+ - [ ] Escrever `html` com Bootstrap
176
+ - [ ] Escrever `script` com AngularJS
177
+ - [ ] Usar `Marketplace.auth.*` para autenticação
178
+ - [ ] Usar `?strict=true` em GETs para preservar tipos BSON
179
+ - [ ] Testar no Studio (navegar até a URL do slug)
180
+ - [ ] Para CRUDs: criar página de lista (display:true) + formulário (display:false)