kamisaku 0.3.4 → 0.4.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +9 -63
- data/examples/birthday_invitation/dino/invitation.pdf +0 -0
- data/lib/kamisaku/arg_parser.rb +4 -0
- data/lib/kamisaku/cli_runner.rb +1 -1
- data/lib/kamisaku/content_validators/base_content_validator.rb +14 -0
- data/lib/kamisaku/content_validators/birthday_invitation_content_validator.rb +218 -0
- data/lib/kamisaku/{content_validator.rb → content_validators/resume_content_validator.rb} +10 -7
- data/lib/kamisaku/html_builder.rb +4 -3
- data/lib/kamisaku/pdf.rb +14 -11
- data/lib/kamisaku/template_helpers.rb +0 -10
- data/lib/kamisaku/version.rb +1 -1
- data/lib/kamisaku.rb +4 -2
- data/lib/schema/birthday_invitation/example.yml +45 -0
- data/lib/schema/birthday_invitation/schema.yml +40 -0
- data/lib/templates/birthday_invitation/dino/template.html.erb +486 -0
- metadata +24 -18
- /data/examples/{chromatic → resume/chromatic}/example.pdf +0 -0
- /data/examples/{gradient → resume/gradient}/example.pdf +0 -0
- /data/examples/{meridian → resume/meridian}/example.pdf +0 -0
- /data/examples/{paper → resume/paper}/example.pdf +0 -0
- /data/examples/{prism → resume/prism}/example.pdf +0 -0
- /data/examples/{sleek → resume/sleek}/example.pdf +0 -0
- /data/examples/{zenith → resume/zenith}/example.pdf +0 -0
- /data/lib/schema/{example.yml → resume/example.yml} +0 -0
- /data/lib/schema/{schema.yml → resume/schema.yml} +0 -0
- /data/lib/templates/{chromatic → resume/chromatic}/template.html.erb +0 -0
- /data/lib/templates/{gradient → resume/gradient}/template.html.erb +0 -0
- /data/lib/templates/{meridian → resume/meridian}/template.html.erb +0 -0
- /data/lib/templates/{paper → resume/paper}/template.html.erb +0 -0
- /data/lib/templates/{prism → resume/prism}/template.html.erb +0 -0
- /data/lib/templates/{sleek → resume/sleek}/template.html.erb +0 -0
- /data/lib/templates/{zenith → resume/zenith}/template.html.erb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0307528fa9400ae092638a768482af0a290b9f7e65f0ca8fcccfa501493eeb92'
|
|
4
|
+
data.tar.gz: bec81b49d0f9a635ad59dcfde08f718a5f8ab5471fa5d9c23eaf37dd2a3789e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0738241ce36d4fb6e43439a35e39614ac935e922247cfb91e87dc3e236b6897f105feee6d827e3341f19286344935e7c37ea19e4705b6c7eb5187c6758049c6f'
|
|
7
|
+
data.tar.gz: 0525b51569873f87984eadd9c779b3ac0254250c541e3fbf6cb37e0f14a746ac3866e2024911f94727f6af40f58afd0758ef6c5faddb915534328e638fa0b27f
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Removed
|
|
15
15
|
|
|
16
|
+
## [0.4.0] - 2025.06.09
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Added new `birthday_invitation` schema type for Birthday Invitation PDF generations
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Updated the CLI tool to accept `category` to generate specific PDF
|
|
23
|
+
|
|
16
24
|
## [0.3.4] - 2025.06.06
|
|
17
25
|
|
|
18
26
|
### Added
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 📃 Kamisaku
|
|
2
2
|
|
|
3
|
-
*Build
|
|
3
|
+
*Build resume PDF from a yaml text file.*
|
|
4
4
|
|
|
5
5
|
🚀 See it in action at [https://kamisaku.sinaru.com/](https://kamisaku.sinaru.com/?utm_source=github).
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
See [examples](/examples) directory for sample generated PDF files based of [templates](/lib/templates).
|
|
12
12
|
|
|
13
13
|
## Templates
|
|
14
|
-
For a list of templates availble for
|
|
14
|
+
For a list of templates availble for PDF generation, check the [examples](/examples) directory where each directory name is a template name.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -42,64 +42,9 @@ $ gem install kamisaku
|
|
|
42
42
|
|
|
43
43
|
## Usage
|
|
44
44
|
|
|
45
|
-
First we need to have a `yaml` file or a string with the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
version: 1
|
|
49
|
-
profile:
|
|
50
|
-
name: # Your name
|
|
51
|
-
title: # Your current job title
|
|
52
|
-
about: # Some sleek details about your experience
|
|
53
|
-
|
|
54
|
-
contact:
|
|
55
|
-
github: # Github username
|
|
56
|
-
mobile: # Mobile number
|
|
57
|
-
email: # email address
|
|
58
|
-
linkedin: # Linkedin username
|
|
59
|
-
location:
|
|
60
|
-
country: # country name
|
|
61
|
-
city: # city name
|
|
62
|
-
|
|
63
|
-
skills:
|
|
64
|
-
- area: # specific skill area you are specialized in
|
|
65
|
-
items:
|
|
66
|
-
- # sub item such as a technology you have the skill in under the specialized area
|
|
67
|
-
|
|
68
|
-
experiences:
|
|
69
|
-
- title: # job title
|
|
70
|
-
organisation: # Name of the place you worked
|
|
71
|
-
location:
|
|
72
|
-
city: # city name
|
|
73
|
-
country: # country name
|
|
74
|
-
from:
|
|
75
|
-
month: # month number that you started
|
|
76
|
-
year: # year number that you started
|
|
77
|
-
# if following is not given, it is assumed you are still working
|
|
78
|
-
to:
|
|
79
|
-
month: # month number that you stopped
|
|
80
|
-
year: # year number that you stopped
|
|
81
|
-
skills:
|
|
82
|
-
- # a short name for a specialized skilled you gained
|
|
83
|
-
achievements:
|
|
84
|
-
- # Things you have achieved or did
|
|
85
|
-
|
|
86
|
-
education:
|
|
87
|
-
- institute: # name of the place you studied
|
|
88
|
-
location:
|
|
89
|
-
city: # city name
|
|
90
|
-
country: # country name
|
|
91
|
-
qualification: # name of the degree/diploma qualification
|
|
92
|
-
field: # name of the field such as Computer Science
|
|
93
|
-
from:
|
|
94
|
-
month: # month number that you started
|
|
95
|
-
year: # year number that you started
|
|
96
|
-
# if following is not given, it is assumed you are still studying
|
|
97
|
-
to:
|
|
98
|
-
month: # month number that you stopped
|
|
99
|
-
year: # year number that you stopped
|
|
100
|
-
achievements:
|
|
101
|
-
- # Things you have achieved or did
|
|
102
|
-
```
|
|
45
|
+
First we need to have a `yaml` file or a string with the correct schema data structure for the type of the PDF you are generating.
|
|
46
|
+
|
|
47
|
+
For the list of schemas, check `lib/schema` folder. E.g. For resume it is `lib/schema/resume/schema.yml`.
|
|
103
48
|
|
|
104
49
|
## Generating PDF
|
|
105
50
|
|
|
@@ -108,13 +53,14 @@ education:
|
|
|
108
53
|
Once you have the YAML text file, feed it into the `bin/console` and specify the output location.
|
|
109
54
|
|
|
110
55
|
```bash
|
|
111
|
-
bin/console -c examples/john_doe.yml -o examples/paper/john_doe.pdf
|
|
56
|
+
bin/console -c examples/john_doe.yml -o examples/paper/john_doe.pdf -k resume -t paper
|
|
112
57
|
```
|
|
113
58
|
|
|
114
59
|
#### Bash options
|
|
115
60
|
|
|
116
61
|
- `-c` the YAML file
|
|
117
62
|
- `-o` output location for the PDF file including the name
|
|
63
|
+
- `-k` the type of document you want to generate
|
|
118
64
|
- `-t` template to use
|
|
119
65
|
|
|
120
66
|
### Using `PDF` class
|
|
@@ -125,9 +71,9 @@ yaml_str = "..."
|
|
|
125
71
|
# Create a hash
|
|
126
72
|
content_hash = Kamisaku::Helpers.yaml_str_to_content_hash(yaml_str)
|
|
127
73
|
# Validate the hash is correct. If there is any issue, it will raise a ` Kamisaku::Error ` exception.
|
|
128
|
-
Kamisaku::
|
|
74
|
+
Kamisaku::BaseContentValidator.new(content_hash:).validate!
|
|
129
75
|
# create a pdf instance
|
|
130
|
-
pdf = Kamisaku::PDF.new(content_hash:, template: "paper")
|
|
76
|
+
pdf = Kamisaku::PDF.new(content_hash:, category: "resume", template: "paper")
|
|
131
77
|
# create the PDF at given path
|
|
132
78
|
pdf.write_to('/path/to/generated_file.pdf')
|
|
133
79
|
```
|
|
Binary file
|
data/lib/kamisaku/arg_parser.rb
CHANGED
|
@@ -20,6 +20,10 @@ module Kamisaku
|
|
|
20
20
|
options[:pdf_file] = pdf_file
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
parser.on("-k", "--category CATEGORY", "Provide the TEMPLATE name") do |category|
|
|
24
|
+
options[:category] = category
|
|
25
|
+
end
|
|
26
|
+
|
|
23
27
|
parser.on("-t", "--template TEMPLATE", "Provide the TEMPLATE name") do |template|
|
|
24
28
|
options[:template] = template
|
|
25
29
|
end
|
data/lib/kamisaku/cli_runner.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Kamisaku
|
|
|
6
6
|
raise Kamisaku::Error.new "YAML file does not exist" unless File.exist?(yaml_file)
|
|
7
7
|
|
|
8
8
|
yaml_string = File.read(yaml_file)
|
|
9
|
-
pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), template: options[:template])
|
|
9
|
+
pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), category: options[:category], template: options[:template])
|
|
10
10
|
pdf.write_to(options[:pdf_file])
|
|
11
11
|
pdf.write_to_html_file(options[:html_output]) if options[:html_output]
|
|
12
12
|
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
module Kamisaku
|
|
2
|
+
class BirthdayInvitationContentValidator < BaseContentValidator
|
|
3
|
+
TEMPLATES = %(
|
|
4
|
+
dino
|
|
5
|
+
).freeze
|
|
6
|
+
|
|
7
|
+
def validate!
|
|
8
|
+
validate_party_details
|
|
9
|
+
validate_venue
|
|
10
|
+
validate_contact_info
|
|
11
|
+
validate_rsvp
|
|
12
|
+
validate_special_instructions if data[:special_instructions]
|
|
13
|
+
validate_activities if data[:activities]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def validate_party_details
|
|
19
|
+
raise Error, "Missing party_details" unless data[:party_details]
|
|
20
|
+
raise Error, "party_details must be a hash" unless data[:party_details].is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
required_fields = %i[person_name age date start_time]
|
|
23
|
+
allowed_fields = %i[person_name age date start_time end_time theme]
|
|
24
|
+
party_fields = data[:party_details].keys
|
|
25
|
+
|
|
26
|
+
unless party_fields.all? { |field| allowed_fields.include?(field) }
|
|
27
|
+
raise Error, "party_details contains invalid fields"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
required_fields.each do |field|
|
|
31
|
+
unless party_fields.include?(field)
|
|
32
|
+
raise Error, "party_details missing required field '#{field}'"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
validate_string_field(data[:party_details][:person_name], "party_details", "person_name")
|
|
37
|
+
validate_integer_field(data[:party_details][:age], "party_details", "age")
|
|
38
|
+
validate_date(data[:party_details][:date], "party_details")
|
|
39
|
+
validate_string_field(data[:party_details][:start_time], "party_details", "start_time")
|
|
40
|
+
|
|
41
|
+
if data[:party_details][:end_time]
|
|
42
|
+
validate_string_field(data[:party_details][:end_time], "party_details", "end_time")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if data[:party_details][:theme]
|
|
46
|
+
validate_string_field(data[:party_details][:theme], "party_details", "theme")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def validate_venue
|
|
51
|
+
raise Error, "Missing venue" unless data[:venue]
|
|
52
|
+
raise Error, "venue must be a hash" unless data[:venue].is_a?(Hash)
|
|
53
|
+
|
|
54
|
+
required_fields = %i[address]
|
|
55
|
+
allowed_fields = %i[name address additional_instructions]
|
|
56
|
+
venue_fields = data[:venue].keys
|
|
57
|
+
|
|
58
|
+
unless venue_fields.all? { |field| allowed_fields.include?(field) }
|
|
59
|
+
raise Error, "venue contains invalid fields"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
required_fields.each do |field|
|
|
63
|
+
unless venue_fields.include?(field)
|
|
64
|
+
raise Error, "venue missing required field '#{field}'"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
validate_string_field(data[:venue][:address], "venue", "address")
|
|
69
|
+
|
|
70
|
+
if data[:venue][:name]
|
|
71
|
+
validate_string_field(data[:venue][:name], "venue", "name")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if data[:venue][:additional_instructions]
|
|
75
|
+
validate_string_field(data[:venue][:additional_instructions], "venue", "additional_instructions")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def validate_contact_info
|
|
80
|
+
raise Error, "Missing contact_info" unless data[:contact_info]
|
|
81
|
+
raise Error, "contact_info must be a hash" unless data[:contact_info].is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
required_fields = %i[host_name phone]
|
|
84
|
+
allowed_fields = %i[host_name phone email]
|
|
85
|
+
contact_fields = data[:contact_info].keys
|
|
86
|
+
|
|
87
|
+
unless contact_fields.all? { |field| allowed_fields.include?(field) }
|
|
88
|
+
raise Error, "contact_info contains invalid fields"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
required_fields.each do |field|
|
|
92
|
+
unless contact_fields.include?(field)
|
|
93
|
+
raise Error, "contact_info missing required field '#{field}'"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
validate_string_field(data[:contact_info][:host_name], "contact_info", "host_name")
|
|
98
|
+
validate_string_field(data[:contact_info][:phone], "contact_info", "phone")
|
|
99
|
+
|
|
100
|
+
if data[:contact_info][:email]
|
|
101
|
+
validate_string_field(data[:contact_info][:email], "contact_info", "email")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def validate_rsvp
|
|
106
|
+
raise Error, "Missing rsvp" unless data[:rsvp]
|
|
107
|
+
raise Error, "rsvp must be a hash" unless data[:rsvp].is_a?(Hash)
|
|
108
|
+
|
|
109
|
+
required_fields = %i[deadline method contact]
|
|
110
|
+
rsvp_fields = data[:rsvp].keys
|
|
111
|
+
|
|
112
|
+
unless rsvp_fields.all? { |field| required_fields.include?(field) }
|
|
113
|
+
raise Error, "rsvp contains invalid fields"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
required_fields.each do |field|
|
|
117
|
+
unless rsvp_fields.include?(field)
|
|
118
|
+
raise Error, "rsvp missing required field '#{field}'"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
validate_date(data[:rsvp][:deadline], "rsvp")
|
|
123
|
+
validate_string_field(data[:rsvp][:method], "rsvp", "method")
|
|
124
|
+
validate_string_field(data[:rsvp][:contact], "rsvp", "contact")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def validate_special_instructions
|
|
128
|
+
raise Error, "special_instructions must be a hash" unless data[:special_instructions].is_a?(Hash)
|
|
129
|
+
|
|
130
|
+
allowed_fields = %i[what_to_bring dress_code food_allergies_note gift_preferences parking_info weather_backup]
|
|
131
|
+
special_fields = data[:special_instructions].keys
|
|
132
|
+
|
|
133
|
+
unless special_fields.all? { |field| allowed_fields.include?(field) }
|
|
134
|
+
raise Error, "special_instructions contains invalid fields"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
data[:special_instructions].each do |field, value|
|
|
138
|
+
validate_string_field(value, "special_instructions", field.to_s)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def validate_activities
|
|
143
|
+
raise Error, "activities must be a hash" unless data[:activities].is_a?(Hash)
|
|
144
|
+
|
|
145
|
+
allowed_fields = %i[main_activities entertainment]
|
|
146
|
+
activities_fields = data[:activities].keys
|
|
147
|
+
|
|
148
|
+
unless activities_fields.all? { |field| allowed_fields.include?(field) }
|
|
149
|
+
raise Error, "activities contains invalid fields"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if data[:activities][:main_activities]
|
|
153
|
+
validate_array_of_strings(data[:activities][:main_activities], "activities", "main_activities")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if data[:activities][:entertainment]
|
|
157
|
+
validate_string_field(data[:activities][:entertainment], "activities", "entertainment")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def validate_date(date, section)
|
|
162
|
+
raise Error, "#{section}: date must be a hash" unless date.is_a?(Hash)
|
|
163
|
+
|
|
164
|
+
required_fields = %i[day month year]
|
|
165
|
+
date_fields = date.keys
|
|
166
|
+
|
|
167
|
+
unless date_fields.all? { |field| required_fields.include?(field) }
|
|
168
|
+
raise Error, "#{section}: date contains invalid fields"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
required_fields.each do |field|
|
|
172
|
+
unless date_fields.include?(field)
|
|
173
|
+
raise Error, "#{section}: date missing required field '#{field}'"
|
|
174
|
+
end
|
|
175
|
+
validate_integer_field(date[field], section, "date.#{field}")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
validate_day_range(date[:day], section)
|
|
179
|
+
validate_month_range(date[:month], section)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def validate_string_field(value, section, field)
|
|
183
|
+
unless value.is_a?(String)
|
|
184
|
+
raise Error, "#{section}: field '#{field}' must be a string"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def validate_integer_field(value, section, field)
|
|
189
|
+
unless value.is_a?(Integer)
|
|
190
|
+
raise Error, "#{section}: field '#{field}' must be an integer"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def validate_array_of_strings(value, section, field)
|
|
195
|
+
unless value.is_a?(Array)
|
|
196
|
+
raise Error, "#{section}: field '#{field}' must be an array"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
value.each do |item|
|
|
200
|
+
unless item.is_a?(String)
|
|
201
|
+
raise Error, "#{section}: each item in '#{field}' must be a string"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def validate_day_range(day, section)
|
|
207
|
+
unless day.between?(1, 31)
|
|
208
|
+
raise Error, "#{section}: day must be between 1-31"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def validate_month_range(month, section)
|
|
213
|
+
unless month.between?(1, 12)
|
|
214
|
+
raise Error, "#{section}: month must be between 1-12"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
module Kamisaku
|
|
2
|
-
class
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
class ResumeContentValidator < Kamisaku::BaseContentValidator
|
|
3
|
+
TEMPLATES = %w[
|
|
4
|
+
paper
|
|
5
|
+
sleek
|
|
6
|
+
zenith
|
|
7
|
+
meridian
|
|
8
|
+
prism
|
|
9
|
+
gradient
|
|
10
|
+
chromatic
|
|
11
|
+
].freeze
|
|
9
12
|
|
|
10
13
|
def validate!
|
|
11
14
|
validate_version
|
|
@@ -2,13 +2,14 @@ require "erb"
|
|
|
2
2
|
|
|
3
3
|
module Kamisaku
|
|
4
4
|
class HtmlBuilder
|
|
5
|
-
attr_reader :content_hash, :template
|
|
5
|
+
attr_reader :content_hash, :category, :template
|
|
6
6
|
alias_method :data, :content_hash
|
|
7
7
|
|
|
8
8
|
include TemplateHelpers
|
|
9
9
|
|
|
10
|
-
def initialize(content_hash, template)
|
|
10
|
+
def initialize(content_hash, category, template)
|
|
11
11
|
@content_hash = content_hash
|
|
12
|
+
@category = category
|
|
12
13
|
@template = template
|
|
13
14
|
end
|
|
14
15
|
|
|
@@ -20,7 +21,7 @@ module Kamisaku
|
|
|
20
21
|
private
|
|
21
22
|
|
|
22
23
|
def template_html
|
|
23
|
-
path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
|
|
24
|
+
path = File.join(File.dirname(__FILE__), "/../templates/#{category}/#{template}/template.html.erb")
|
|
24
25
|
File.read(path)
|
|
25
26
|
end
|
|
26
27
|
end
|
data/lib/kamisaku/pdf.rb
CHANGED
|
@@ -2,14 +2,22 @@ require "tempfile"
|
|
|
2
2
|
|
|
3
3
|
module Kamisaku
|
|
4
4
|
class PDF
|
|
5
|
-
attr_reader :content_hash, :template
|
|
5
|
+
attr_reader :content_hash, :category, :template
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
CONTENT_VALIDATOR_MAP = {
|
|
8
|
+
resume: ResumeContentValidator,
|
|
9
|
+
birthday_invitation: BirthdayInvitationContentValidator
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def initialize(content_hash:, category:, template:)
|
|
8
13
|
@content_hash = content_hash
|
|
9
|
-
@
|
|
10
|
-
|
|
14
|
+
@category = category
|
|
15
|
+
@template = template
|
|
11
16
|
raise Error, "Invalid template name '#{template}'" unless template.is_a?(String)
|
|
12
|
-
|
|
17
|
+
validator_klass = CONTENT_VALIDATOR_MAP[category.to_sym]
|
|
18
|
+
raise Error, "Invalid template name '#{category}'" unless validator_klass
|
|
19
|
+
validator_klass.new(content_hash:).validate!
|
|
20
|
+
raise Error, "Invalid template name '#{template}'" unless validator_klass::TEMPLATES.include?(template)
|
|
13
21
|
end
|
|
14
22
|
|
|
15
23
|
def write_to(pdf_location)
|
|
@@ -44,13 +52,8 @@ module Kamisaku
|
|
|
44
52
|
def html
|
|
45
53
|
return @html if defined? @html
|
|
46
54
|
|
|
47
|
-
builder = HtmlBuilder.new(content_hash, template)
|
|
55
|
+
builder = HtmlBuilder.new(content_hash, category, template)
|
|
48
56
|
@html = builder.html
|
|
49
57
|
end
|
|
50
|
-
|
|
51
|
-
def template_html
|
|
52
|
-
path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
|
|
53
|
-
File.read(path)
|
|
54
|
-
end
|
|
55
58
|
end
|
|
56
59
|
end
|
data/lib/kamisaku/version.rb
CHANGED
data/lib/kamisaku.rb
CHANGED
|
@@ -2,9 +2,11 @@ require_relative "kamisaku/version"
|
|
|
2
2
|
require_relative "kamisaku/errors"
|
|
3
3
|
require_relative "kamisaku/helpers"
|
|
4
4
|
require_relative "kamisaku/arg_parser"
|
|
5
|
-
require_relative "kamisaku/pdf"
|
|
6
5
|
require_relative "kamisaku/chrome_runner"
|
|
7
6
|
require_relative "kamisaku/cli_runner"
|
|
8
7
|
require_relative "kamisaku/template_helpers"
|
|
9
8
|
require_relative "kamisaku/html_builder"
|
|
10
|
-
require_relative "kamisaku/
|
|
9
|
+
require_relative "kamisaku/content_validators/base_content_validator"
|
|
10
|
+
require_relative "kamisaku/content_validators/resume_content_validator"
|
|
11
|
+
require_relative "kamisaku/content_validators/birthday_invitation_content_validator"
|
|
12
|
+
require_relative "kamisaku/pdf"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
party_details:
|
|
3
|
+
person_name: "Emma"
|
|
4
|
+
age: 6
|
|
5
|
+
date:
|
|
6
|
+
day: 15
|
|
7
|
+
month: 8
|
|
8
|
+
year: 2025
|
|
9
|
+
start_time: "2:00 PM"
|
|
10
|
+
end_time: "5:00 PM"
|
|
11
|
+
theme: "Dinosaur Adventure"
|
|
12
|
+
|
|
13
|
+
venue:
|
|
14
|
+
name: "Dino Discovery Park"
|
|
15
|
+
address: "123 Prehistoric Lane, Jurassic Heights, CA 90210"
|
|
16
|
+
additional_instructions: "Enter through the main gate and follow the dinosaur footprints to the party pavilion"
|
|
17
|
+
|
|
18
|
+
contact_info:
|
|
19
|
+
host_name: "Sarah Johnson"
|
|
20
|
+
phone: "(555) 123-4567"
|
|
21
|
+
email: "sarah.johnson@email.com"
|
|
22
|
+
|
|
23
|
+
rsvp:
|
|
24
|
+
deadline:
|
|
25
|
+
day: 8
|
|
26
|
+
month: 8
|
|
27
|
+
year: 2025
|
|
28
|
+
method: "phone or text"
|
|
29
|
+
contact: "Sarah Johnson"
|
|
30
|
+
|
|
31
|
+
special_instructions:
|
|
32
|
+
what_to_bring: "Comfortable shoes for outdoor activities and a water bottle"
|
|
33
|
+
dress_code: "Dinosaur costumes encouraged! Or wear green and brown colors"
|
|
34
|
+
food_allergies_note: "Please let us know about any food allergies when you RSVP"
|
|
35
|
+
gift_preferences: "Books about dinosaurs or outdoor toys would be wonderful!"
|
|
36
|
+
parking_info: "Free parking available in the north lot"
|
|
37
|
+
weather_backup: "Indoor pavilion available in case of rain"
|
|
38
|
+
|
|
39
|
+
activities:
|
|
40
|
+
main_activities:
|
|
41
|
+
- "Dinosaur fossil dig"
|
|
42
|
+
- "Pin the tail on the T-Rex"
|
|
43
|
+
- "Dinosaur egg hunt"
|
|
44
|
+
- "Prehistoric crafts station"
|
|
45
|
+
entertainment: "Dr. Dino the Paleontologist with live dinosaur show"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
party_details: # required: Core information about the birthday party
|
|
2
|
+
person_name: # required: Name of the birthday person
|
|
3
|
+
age: # required: Age the birthday person is turning
|
|
4
|
+
date: # required: Date of the party
|
|
5
|
+
day: # required: Day between 1-31
|
|
6
|
+
month: # required: Month between 1-12
|
|
7
|
+
year: # required: year
|
|
8
|
+
start_time: # required: Party start time
|
|
9
|
+
end_time: # optional: Party end time
|
|
10
|
+
theme: # optional: Party theme (e.g., "Princess", "Superhero", "Dinosaurs")
|
|
11
|
+
|
|
12
|
+
venue: # required: Where the party will be held
|
|
13
|
+
name: # optional: Name of the venue (e.g., "Chuck E. Cheese", "Community Center")
|
|
14
|
+
address: # required: Full address of the party location
|
|
15
|
+
additional_instructions: # optional: Parking info, entrance details, etc.
|
|
16
|
+
|
|
17
|
+
contact_info: # required: How to reach the host
|
|
18
|
+
host_name: # required: Name of parent/guardian hosting
|
|
19
|
+
phone: # required: Contact phone number
|
|
20
|
+
email: # optional: Contact email address
|
|
21
|
+
|
|
22
|
+
rsvp: # required: Response information
|
|
23
|
+
deadline: # required: RSVP deadline date
|
|
24
|
+
day: # required: Day between 1-31
|
|
25
|
+
month: # required: Month between 1-12
|
|
26
|
+
year: # required: year
|
|
27
|
+
method: # required: How to RSVP (phone, email, text)
|
|
28
|
+
contact: # required: Who to contact for RSVP
|
|
29
|
+
|
|
30
|
+
special_instructions: # optional: Additional party details
|
|
31
|
+
what_to_bring: # optional: Items guests should bring
|
|
32
|
+
dress_code: # optional: Costume requirements or dress suggestions
|
|
33
|
+
food_allergies_note: # optional: Note about food allergies
|
|
34
|
+
gift_preferences: # optional: Gift suggestions or "no gifts please"
|
|
35
|
+
parking_info: # optional: Parking instructions
|
|
36
|
+
weather_backup: # optional: Indoor backup plan for outdoor parties
|
|
37
|
+
|
|
38
|
+
activities: # optional: Planned party activities
|
|
39
|
+
main_activities: # optional: List of planned activities
|
|
40
|
+
entertainment: # optional: Hired entertainment (clown, magician, etc.)
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Birthday Party Invitation</title>
|
|
7
|
+
<style>
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=Fredoka+One:wght@400&family=Nunito:wght@400;600;700&display=swap');
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Base print and preview styles */
|
|
17
|
+
@page {
|
|
18
|
+
margin: 0;
|
|
19
|
+
width: 5in;
|
|
20
|
+
height: 7in;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media screen {
|
|
24
|
+
body {
|
|
25
|
+
background: #f8fafc;
|
|
26
|
+
}
|
|
27
|
+
.paper {
|
|
28
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media screen, print {
|
|
33
|
+
.paper {
|
|
34
|
+
background: white;
|
|
35
|
+
margin: 0 auto;
|
|
36
|
+
width: 5in;
|
|
37
|
+
height: 7in;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.paper {
|
|
42
|
+
background: linear-gradient(135deg, #C8E6C9 0%, #A5D6A7 50%, #81C784 100%);
|
|
43
|
+
font-family: 'Nunito', sans-serif;
|
|
44
|
+
color: #2E7D32;
|
|
45
|
+
position: relative;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.invitation-container {
|
|
50
|
+
width: 100%;
|
|
51
|
+
height: 100%;
|
|
52
|
+
position: relative;
|
|
53
|
+
background: rgba(255, 255, 255, 0.95);
|
|
54
|
+
border-radius: 20px;
|
|
55
|
+
border: 4px solid #4CAF50;
|
|
56
|
+
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
|
57
|
+
padding: 0.3in;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.decoration-top-right {
|
|
64
|
+
position: absolute;
|
|
65
|
+
top: 0.1in;
|
|
66
|
+
right: -0.4in;
|
|
67
|
+
width: 200px;
|
|
68
|
+
height: 200px;
|
|
69
|
+
transform: scalex(-1);
|
|
70
|
+
z-index: 10;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Decorative dinosaurs */
|
|
74
|
+
.dino-top-left {
|
|
75
|
+
position: absolute;
|
|
76
|
+
top: -0.1in;
|
|
77
|
+
left: -0.4in;
|
|
78
|
+
width: 200px;
|
|
79
|
+
height: auto;
|
|
80
|
+
transform: rotate(-10deg) scalex(-1);
|
|
81
|
+
z-index: 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dino-bottom-right {
|
|
85
|
+
position: absolute;
|
|
86
|
+
bottom: -0.8in;
|
|
87
|
+
right: 1.1in;
|
|
88
|
+
width: 55px;
|
|
89
|
+
height: auto;
|
|
90
|
+
transform: rotate(-15deg);
|
|
91
|
+
z-index: 10;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Balloons */
|
|
95
|
+
.balloon {
|
|
96
|
+
position: absolute;
|
|
97
|
+
font-size: 1.5em;
|
|
98
|
+
animation: float 3s ease-in-out infinite;
|
|
99
|
+
z-index: 5;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.balloon-1 { top: 1.2in; left: 0.8in; animation-delay: 0s; color: #E91E63; }
|
|
103
|
+
.balloon-2 { top: 1.8in; right: 0.6in; animation-delay: 1s; color: #2196F3; }
|
|
104
|
+
.balloon-3 { bottom: 2.5in; left: 0.5in; animation-delay: 2s; color: #FF9800; }
|
|
105
|
+
.balloon-4 { top: 2.8in; right: 1in; animation-delay: 1.5s; color: #9C27B0; }
|
|
106
|
+
|
|
107
|
+
/* Tropical leaves */
|
|
108
|
+
.leaf {
|
|
109
|
+
position: absolute;
|
|
110
|
+
font-size: 1.8em;
|
|
111
|
+
color: #2E7D32;
|
|
112
|
+
z-index: 1;
|
|
113
|
+
opacity: 0.7;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.leaf-1 { top: 0.5in; left: 1.5in; transform: rotate(-20deg); }
|
|
117
|
+
.leaf-2 { top: 1.5in; right: 1.2in; transform: rotate(45deg); }
|
|
118
|
+
.leaf-3 { bottom: 1in; left: 1in; transform: rotate(-45deg); }
|
|
119
|
+
.leaf-4 { bottom: 2.5in; right: 0.3in; transform: rotate(30deg); }
|
|
120
|
+
|
|
121
|
+
/* Stars decoration */
|
|
122
|
+
.star {
|
|
123
|
+
position: absolute;
|
|
124
|
+
color: #FFD700;
|
|
125
|
+
font-size: 0.8em;
|
|
126
|
+
animation: twinkle 2s infinite;
|
|
127
|
+
z-index: 5;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.star-1 { top: 1.2in; left: 2.2in; animation-delay: 0s; }
|
|
131
|
+
.star-2 { top: 3in; left: 3.8in; animation-delay: 0.7s; }
|
|
132
|
+
.star-3 { top: 4.5in; left: 1.8in; animation-delay: 1.4s; }
|
|
133
|
+
.star-4 { top: 5.8in; left: 3.5in; animation-delay: 2.1s; }
|
|
134
|
+
|
|
135
|
+
@keyframes twinkle {
|
|
136
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
137
|
+
50% { opacity: 0.5; transform: scale(1.2); }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Typography */
|
|
141
|
+
.header {
|
|
142
|
+
text-align: center;
|
|
143
|
+
margin-bottom: 0.15in;
|
|
144
|
+
z-index: 20;
|
|
145
|
+
position: relative;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.title {
|
|
149
|
+
font-family: 'Fredoka One', cursive;
|
|
150
|
+
font-size: 22px;
|
|
151
|
+
color: #FF6B35;
|
|
152
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
|
153
|
+
margin-bottom: 5px;
|
|
154
|
+
line-height: 1.1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.child-name {
|
|
158
|
+
font-family: 'Fredoka One', cursive;
|
|
159
|
+
font-size: 26px;
|
|
160
|
+
color: #FF5722;
|
|
161
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
|
162
|
+
margin-bottom: 3px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.age {
|
|
166
|
+
font-size: 18px;
|
|
167
|
+
color: #E65100;
|
|
168
|
+
font-weight: 700;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Content sections */
|
|
172
|
+
.content {
|
|
173
|
+
flex: 1;
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
justify-content: center;
|
|
177
|
+
z-index: 20;
|
|
178
|
+
position: relative;
|
|
179
|
+
margin: 0.1in 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.info-section {
|
|
183
|
+
background: rgba(255, 255, 255, 0.9);
|
|
184
|
+
border-radius: 12px;
|
|
185
|
+
padding: 10px;
|
|
186
|
+
margin: 6px 0;
|
|
187
|
+
border: 2px solid #4CAF50;
|
|
188
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.info-label {
|
|
192
|
+
font-weight: 700;
|
|
193
|
+
color: #2E7D32;
|
|
194
|
+
font-size: 13px;
|
|
195
|
+
display: block;
|
|
196
|
+
margin-bottom: 3px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.info-value {
|
|
200
|
+
color: #1B5E20;
|
|
201
|
+
font-size: 12px;
|
|
202
|
+
line-height: 1.4;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.theme-highlight {
|
|
206
|
+
background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);
|
|
207
|
+
border: 2px solid #FF8F00;
|
|
208
|
+
text-align: center;
|
|
209
|
+
font-weight: 700;
|
|
210
|
+
color: #E65100;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.rsvp-section {
|
|
214
|
+
background: linear-gradient(135deg, #FCE4EC 0%, #F8BBD9 50%);
|
|
215
|
+
border: 2px solid #E91E63;
|
|
216
|
+
text-align: center;
|
|
217
|
+
margin-top: 0.1in;
|
|
218
|
+
z-index: 20;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.footer-deco {
|
|
222
|
+
text-align: center;
|
|
223
|
+
font-size: 1.2em;
|
|
224
|
+
margin-top: 8px;
|
|
225
|
+
z-index: 20;
|
|
226
|
+
position: relative;
|
|
227
|
+
color: #4CAF50;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Print optimizations */
|
|
231
|
+
@media print {
|
|
232
|
+
body {
|
|
233
|
+
margin: 0;
|
|
234
|
+
-webkit-print-color-adjust: exact;
|
|
235
|
+
color-adjust: exact;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
</style>
|
|
239
|
+
</head>
|
|
240
|
+
<body>
|
|
241
|
+
<div class="paper">
|
|
242
|
+
<div class="invitation-container">
|
|
243
|
+
<!-- Decorative dinosaur images -->
|
|
244
|
+
<div class="dino-top-left">
|
|
245
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
246
|
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
247
|
+
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
248
|
+
viewBox="0 0 382.325 382.325" xml:space="preserve">
|
|
249
|
+
<g id="XMLID_800_">
|
|
250
|
+
<path id="XMLID_801_" style="fill:#DD916F;" d="M182.966,228.544v36h-4c-6.6,0-12,5.4-12,12c0,6.6,5.4,12,12,12h4h24v-24v-36
|
|
251
|
+
H182.966z"/>
|
|
252
|
+
<g id="XMLID_802_">
|
|
253
|
+
|
|
254
|
+
<rect id="XMLID_52_" x="221.674" y="222.703" transform="matrix(0.8321 -0.5547 0.5547 0.8321 -100.9291 172.0605)" style="fill:#DD916F;" width="24" height="60"/>
|
|
255
|
+
<path id="XMLID_51_" style="fill:#DD916F;" d="M246.987,251.039l-23.297,15.532c-5.492,3.661-6.989,11.149-3.328,16.641
|
|
256
|
+
c3.661,5.492,11.149,6.989,16.641,3.328l23.297-15.532L246.987,251.039z"/>
|
|
257
|
+
</g>
|
|
258
|
+
<polygon id="XMLID_803_" style="fill:#D87D60;" points="191.163,228.544 191.163,288.544 206.966,288.544 206.966,264.544
|
|
259
|
+
206.966,228.544 "/>
|
|
260
|
+
<path id="XMLID_804_" style="fill:#D87D60;" d="M203.078,124.128c-9.166,0-16.59,2.153-22.696,5.62
|
|
261
|
+
c-1.335-5.063-1.692-9.868-1.692-12.65c0-9.402,17.057-23.317,23.007-23.317c5.949,0,23.006,13.915,23.006,23.317
|
|
262
|
+
c0,2.789-0.359,7.609-1.701,12.684C217.433,126.322,211.146,124.128,203.078,124.128z"/>
|
|
263
|
+
<path id="XMLID_805_" style="fill:#D46C5B;" d="M201.697,93.781c-2.354,0-6.447,2.183-10.535,5.398v26.329
|
|
264
|
+
c3.613-0.882,7.562-1.381,11.916-1.381c8.068,0,14.355,2.194,19.924,5.654c1.343-5.075,1.701-9.896,1.701-12.685
|
|
265
|
+
C224.704,107.696,207.647,93.781,201.697,93.781z"/>
|
|
266
|
+
<path id="XMLID_46_" style="fill:#D46C5B;" d="M238.713,136.213c-6.482-6.482-13.253-10.208-20.022-12.075
|
|
267
|
+
c2.636-4.524,5.781-8.174,7.749-10.141c6.648-6.648,28.549-4.426,32.756-0.219c4.207,4.207,6.429,26.107-0.219,32.755
|
|
268
|
+
c-1.972,1.972-5.634,5.127-10.172,7.766C247.311,147.914,244.417,141.917,238.713,136.213z"/>
|
|
269
|
+
<path id="XMLID_45_" style="fill:#D46C5B;" d="M264.213,166.213c-6.482-6.482-13.253-10.208-20.022-12.075
|
|
270
|
+
c2.636-4.524,5.781-8.174,7.749-10.141c6.648-6.648,28.549-4.426,32.756-0.219c4.207,4.207,6.429,26.107-0.219,32.755
|
|
271
|
+
c-1.972,1.972-5.634,5.127-10.172,7.766C272.812,177.914,269.918,171.917,264.213,166.213z"/>
|
|
272
|
+
<path id="XMLID_44_" style="fill:#D46C5B;" d="M290.847,185.83c-3.935-3.936-8.047-6.198-12.157-7.331
|
|
273
|
+
c1.6-2.747,3.51-4.963,4.705-6.158c4.036-4.036,17.334-2.687,19.889-0.133c2.554,2.554,3.903,15.852-0.133,19.888
|
|
274
|
+
c-1.197,1.197-3.421,3.113-6.176,4.715C296.068,192.935,294.311,189.294,290.847,185.83z"/>
|
|
275
|
+
<path id="XMLID_43_" style="fill:#D87D60;" d="M165.12,134.259c-6.482,6.482-10.208,13.253-12.075,20.023
|
|
276
|
+
c-4.524-2.636-8.174-5.781-10.141-7.749c-6.648-6.648-4.426-28.549-0.219-32.756c4.207-4.207,26.107-6.429,32.755,0.219
|
|
277
|
+
c1.972,1.972,5.127,5.634,7.766,10.172C176.822,125.66,170.825,128.554,165.12,134.259z"/>
|
|
278
|
+
<path id="XMLID_42_" style="fill:#D87D60;" d="M134.156,153.693c-5.209,5.209-8.203,10.65-9.703,16.09
|
|
279
|
+
c-3.635-2.118-6.569-4.646-8.15-6.227c-5.342-5.342-3.557-22.941-0.176-26.322c3.381-3.381,20.98-5.166,26.322,0.176
|
|
280
|
+
c1.585,1.585,4.12,4.527,6.241,8.174C143.559,146.783,138.74,149.108,134.156,153.693z"/>
|
|
281
|
+
<g id="XMLID_806_">
|
|
282
|
+
<path id="XMLID_41_" style="fill:#DD916F;" d="M128.159,212.031c-0.909,1.035-8.937,10.623-11.007,28.292
|
|
283
|
+
c-1.939,16.551,1.542,23.371,4.805,26.177c3.538,3.043,8.87,2.642,11.913-0.895c2.664-3.097,0.445-6.75,0.278-10.679
|
|
284
|
+
c-1.15-27.024,15.928-25.551,16.153-25.82l-9.444-5.929L128.159,212.031z"/>
|
|
285
|
+
</g>
|
|
286
|
+
<path id="XMLID_39_" style="fill:#DD916F;" d="M55.199,115.9c1.58,16.485,11.641,2.603,34.471,20s31.966,16.843,39.02,13.882
|
|
287
|
+
c37.37-15.686,43.5-38,88.632-30c44.701,7.924,46.089,37.5,57.017,50c24.166,27.642,84.576,5.667,104.252,40.149
|
|
288
|
+
c12.588,22.061-10.818,62.436-9.223,49.851c5.068-40-4.947-53.314-38.012-50s-57.6,21.657-85.526,33.125
|
|
289
|
+
c-42.784,17.569-93.466-2.431-110.214-16.569c-28.501-24.059-65.46-62.39-80.417-60.733c-14.958,1.657-28.713,5.4-37.438,4.177
|
|
290
|
+
c-7.803-1.094-26.441-8.319-13.058-20.745S53.238,95.449,55.199,115.9z"/>
|
|
291
|
+
<g id="XMLID_807_">
|
|
292
|
+
<path id="XMLID_38_" style="fill:#DD916F;" d="M111.653,189.782c-1.174,0.773-11.663,8.04-18.474,24.867
|
|
293
|
+
c-6.379,15.763-4.795,23.415-2.343,27.057c2.66,3.95,8.015,4.995,11.964,2.337c3.458-2.328,2.262-6.522,3.158-10.434
|
|
294
|
+
c6.168-26.904,22.576-20.841,22.87-21.045l-7.692-8.386L111.653,189.782z"/>
|
|
295
|
+
</g>
|
|
296
|
+
<circle id="XMLID_36_" style="fill:#FFE477;" cx="193.104" cy="162.159" r="10"/>
|
|
297
|
+
<path id="XMLID_808_" style="fill:#D87D60;" d="M378.591,209.931c-19.676-34.482-80.086-12.507-104.252-40.149
|
|
298
|
+
c-10.928-12.5-12.316-42.076-57.017-50c-10.553-1.871-18.973-2.083-26.159-1.111v129.743c17.572,2.602,36.805,1.828,54.668-5.507
|
|
299
|
+
c27.926-11.468,52.462-29.811,85.526-33.125c33.064-3.314,43.08,10,38.012,50C367.773,272.368,391.179,231.992,378.591,209.931z"/>
|
|
300
|
+
<circle id="XMLID_34_" style="fill:#F6E277;" cx="225.999" cy="149.199" r="15"/>
|
|
301
|
+
<circle id="XMLID_33_" style="fill:#F6E277;" cx="248.688" cy="179.78" r="10"/>
|
|
302
|
+
<path id="XMLID_809_" d="M44.69,137.782c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6S47.999,137.782,44.69,137.782z"/>
|
|
303
|
+
<path id="XMLID_810_" style="fill:#F6E277;" d="M202.833,159.846c-1.248-5.249-6.423-8.527-11.671-7.484v19.6
|
|
304
|
+
c1.367,0.272,2.81,0.27,4.255-0.073C200.791,170.611,204.111,165.219,202.833,159.846z"/>
|
|
305
|
+
</g>
|
|
306
|
+
</svg>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div class="dino-bottom-right">
|
|
310
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
311
|
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
312
|
+
<svg height="200px" width="200px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
313
|
+
viewBox="0 0 353.123 353.123" xml:space="preserve">
|
|
314
|
+
<g id="XMLID_463_">
|
|
315
|
+
<path id="XMLID_464_" style="fill:#D570A7;" d="M104.581,57.7c4.223,21.21,10.669,46.599,20.944,69.996
|
|
316
|
+
c5.562-2.582,13.012-11.275,10.552-16.799c-1.799-4.038-8.399-5.271-14.023-4.704c5.212-3.253,10.759-9.974,8.939-14.812
|
|
317
|
+
c-1.724-4.585-9.539-6.081-15.514-5.385c5.299-3.049,10.975-9.372,9.418-14.222c-1.451-4.517-8.75-6.42-14.661-6.171
|
|
318
|
+
c5.242-3.091,10.762-9.319,9.223-14.109c-1.451-4.516-8.749-6.42-14.66-6.171c5.242-3.091,10.761-9.318,9.222-14.109
|
|
319
|
+
c-1.768-5.503-12.216-7.126-18.258-5.701C99.738,35.493,102.327,46.38,104.581,57.7z"/>
|
|
320
|
+
<path id="XMLID_42_" style="fill:#DA88B4;" d="M133.291,303.471l-2.898-0.826c-5.201-1.482-8.216-6.9-6.734-12.101l11.585-51.057
|
|
321
|
+
l21.733,6.193l-11.585,51.057C143.91,301.938,138.492,304.953,133.291,303.471z"/>
|
|
322
|
+
<path id="XMLID_41_" style="fill:#DA88B4;" d="M163.564,303.709h-3.013c-5.408,0-9.792-4.384-9.792-9.792l3.205-52.228h22.598
|
|
323
|
+
l-3.205,52.228C173.357,299.324,168.972,303.709,163.564,303.709z"/>
|
|
324
|
+
<path id="XMLID_40_" style="fill:#D570A7;" d="M249.704,303.471l2.898-0.826c5.201-1.482,8.216-6.9,6.734-12.101l-11.585-51.057
|
|
325
|
+
l-21.733,6.193l11.585,51.057C239.085,301.938,244.503,304.953,249.704,303.471z"/>
|
|
326
|
+
<path id="XMLID_39_" style="fill:#D570A7;" d="M219.43,303.709h3.013c5.408,0,9.792-4.384,9.792-9.792l-3.205-52.228h-22.598
|
|
327
|
+
l3.205,52.228C209.638,299.324,214.022,303.709,219.43,303.709z"/>
|
|
328
|
+
<path id="XMLID_466_" style="fill:#DA88B4;" d="M286.793,238.75c-27.199-27.357-30.171-49.76-88.887-79.118
|
|
329
|
+
c-36.069-18.034-64.83-21.45-72.108-41.205c-14.422-39.144-19.687-64.375-23.605-77.485c-1.183-4.131-4.209-33.439-38.202-31.894
|
|
330
|
+
c-17.067,0-30.903,9.224-30.903,20.602s13.836,20.602,30.903,20.602c6.501,0,19.229,5.337,20.602,16.668
|
|
331
|
+
c4.859,40.089,5.666,45.325,19.152,97.392c9.798,37.826,2.137,52.661,30.947,81.517c44.36,44.429,115.945,24.135,122.519,24.135
|
|
332
|
+
c0.98-0.125,43.589,19.794,30.495,49.064c-11.181,24.994-115.189,9.073-134.451,6.551c-53.211-6.967,48.365,39.132,139.027,24.742
|
|
333
|
+
C336.693,343.274,322.526,274.69,286.793,238.75z"/>
|
|
334
|
+
<path id="XMLID_467_" style="fill:#D570A7;" d="M286.793,238.75c-27.199-27.357-30.171-49.76-88.887-79.118
|
|
335
|
+
c-7.473-3.736-14.625-6.84-21.344-9.596v119.823c37.636,10.585,75.926,0.107,80.65,0.107c0.98-0.125,43.589,19.794,30.495,49.064
|
|
336
|
+
c-8.708,19.465-73.712,14.115-111.146,9.604v14.248c30.189,7.904,74.1,14.048,115.721,7.442
|
|
337
|
+
C336.693,343.274,322.526,274.69,286.793,238.75z"/>
|
|
338
|
+
<path id="XMLID_36_" style="fill:#DA88B4;" d="M51.455,25.024l0.147-19.846c0.03-4.009,4.388-6.483,7.845-4.453l17.114,10.05
|
|
339
|
+
L51.455,25.024z"/>
|
|
340
|
+
<path id="XMLID_468_" d="M60.561,15.024c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6S63.87,15.024,60.561,15.024z"/>
|
|
341
|
+
<circle id="XMLID_469_" style="fill:#98C79C;" cx="214.578" cy="217.007" r="8.017"/>
|
|
342
|
+
<circle id="XMLID_470_" style="fill:#98C79C;" cx="223.646" cy="192.108" r="7.085"/>
|
|
343
|
+
<circle id="XMLID_472_" style="fill:#98C79C;" cx="236.393" cy="215.214" r="9.695"/>
|
|
344
|
+
<circle id="XMLID_473_" style="fill:#98C79C;" cx="196.866" cy="194.72" r="9.695"/>
|
|
345
|
+
</g>
|
|
346
|
+
</svg>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<div class="decoration-top-right">
|
|
350
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
351
|
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
352
|
+
<svg viewBox="0 -3.57 537.296 537.296" xmlns="http://www.w3.org/2000/svg">
|
|
353
|
+
<g id="Group_10" data-name="Group 10" transform="translate(-1056.208 1535.36)">
|
|
354
|
+
<path id="path232" d="M1067.7-1116.731a8.07,8.07,0,0,1-10.751-3.987,8.194,8.194,0,0,1,3.965-10.84q142.406-66.26,234.63-165.839,92.092-99.44,134.341-232.293a8.075,8.075,0,0,1,10.173-5.28,8.191,8.191,0,0,1,5.24,10.267q-43.36,136.36-137.89,238.439Q1213-1184.324,1067.7-1116.731" fill="#465da9" fill-rule="evenodd"/>
|
|
355
|
+
<path id="path234" d="M1367.5-1168.97l2.027-36.693a12.961,12.961,0,0,1,11.093-12.227l36.066-5.32a12.766,12.766,0,0,0,10.547-9.12,12.925,12.925,0,0,0-3.654-13.507l-93.665-86.32a12.95,12.95,0,0,0-18.357.827l-60.276,66.467a13.2,13.2,0,0,0,.823,18.507l93.662,86.32a12.669,12.669,0,0,0,13.667,2.467,12.84,12.84,0,0,0,8.067-11.4" fill="#f0393c" fill-rule="evenodd"/>
|
|
356
|
+
<path id="path236" d="M1486.2-1324.437l-5.307-36.346a13,13,0,0,1,8.44-14.227l34.267-12.507a12.815,12.815,0,0,0,8.52-11.053,12.884,12.884,0,0,0-6.267-12.506l-108.933-65.64a12.965,12.965,0,0,0-17.813,4.52l-45.84,77.293a13.179,13.179,0,0,0,4.493,17.96l108.92,65.653a12.672,12.672,0,0,0,13.88-.347,12.882,12.882,0,0,0,5.64-12.8" fill="#f9b317" fill-rule="evenodd"/>
|
|
357
|
+
<path id="path238" d="M1214.952-1049.931,1225-1085.237a12.893,12.893,0,0,1,13.507-9.453l36.336,2.88a12.72,12.72,0,0,0,12.284-6.533,12.919,12.919,0,0,0-.592-13.987l-72.341-105.12a12.964,12.964,0,0,0-18.083-3.293l-73.394,51.333a13.193,13.193,0,0,0-3.269,18.227l72.343,105.12a12.7,12.7,0,0,0,12.792,5.453,12.766,12.766,0,0,0,10.372-9.32" fill="#43bb4e" fill-rule="evenodd"/>
|
|
358
|
+
<path id="path240" d="M1132.248-1005.211a17.976,17.976,0,0,0,17.867-18.013,17.976,17.976,0,0,0-17.867-18.013,17.977,17.977,0,0,0-17.868,18.013,17.977,17.977,0,0,0,17.868,18.013" fill="#f9b317" fill-rule="evenodd"/>
|
|
359
|
+
<path id="path242" d="M1346.637-1465.236a11.506,11.506,0,0,0,11.44-11.52,11.518,11.518,0,0,0-11.44-11.533,11.506,11.506,0,0,0-11.427,11.533,11.5,11.5,0,0,0,11.427,11.52" fill="#43bb4e" fill-rule="evenodd"/>
|
|
360
|
+
<path id="path244" d="M1368.8-1100.571a4.47,4.47,0,0,1,3.6-5.16,4.419,4.419,0,0,1,5.12,3.627l3.333,19.333a4.45,4.45,0,0,1-3.6,5.16,4.439,4.439,0,0,1-5.12-3.64l-3.333-19.32" fill="#5b7fc9" fill-rule="evenodd"/>
|
|
361
|
+
<path id="path246" d="M1375.17-1063.651a4.456,4.456,0,0,1,3.6-5.173,4.453,4.453,0,0,1,5.12,3.64l3.333,19.32a4.463,4.463,0,0,1-3.6,5.16,4.419,4.419,0,0,1-5.12-3.627l-3.333-19.32" fill="#5b7fc9" fill-rule="evenodd"/>
|
|
362
|
+
<path id="path248" d="M1350.877-1063.931a4.42,4.42,0,0,1-5.12-3.627,4.443,4.443,0,0,1,3.6-5.16l19.173-3.36a4.433,4.433,0,0,1,5.12,3.627,4.469,4.469,0,0,1-3.613,5.16l-19.16,3.36" fill="#5b7fc9" fill-rule="evenodd"/>
|
|
363
|
+
<path id="path250" d="M1387.49-1070.344a4.438,4.438,0,0,1-5.12-3.64,4.47,4.47,0,0,1,3.6-5.16l19.173-3.36a4.426,4.426,0,0,1,5.12,3.627,4.476,4.476,0,0,1-3.6,5.173l-19.173,3.36" fill="#5b7fc9" fill-rule="evenodd"/>
|
|
364
|
+
<path id="path252" d="M1228.576-1375.517a50.6,50.6,0,0,1,27.487-29.36,3.669,3.669,0,0,0,2.247-3.533,3.649,3.649,0,0,0-2.447-3.4,50.559,50.559,0,0,1-29.125-27.707,3.609,3.609,0,0,0-3.511-2.267,3.626,3.626,0,0,0-3.373,2.467,50.6,50.6,0,0,1-27.488,29.36,3.665,3.665,0,0,0-2.244,3.533,3.671,3.671,0,0,0,2.445,3.4,50.5,50.5,0,0,1,29.125,27.706,3.646,3.646,0,0,0,3.511,2.267,3.626,3.626,0,0,0,3.373-2.467" fill="#5b7fc9" fill-rule="evenodd"/>
|
|
365
|
+
<path id="path254" d="M1238.449-1422.623q-1.824,25.84-28.471,30.413a50.582,50.582,0,0,1,11.713,16.893,3.646,3.646,0,0,0,3.511,2.267,3.626,3.626,0,0,0,3.373-2.467,50.6,50.6,0,0,1,27.487-29.36,3.669,3.669,0,0,0,2.247-3.533,3.649,3.649,0,0,0-2.447-3.4,50.248,50.248,0,0,1-17.413-10.813" fill="#4267ba" fill-rule="evenodd"/>
|
|
366
|
+
<path id="path256" d="M1563.769-1449.77a50.566,50.566,0,0,1,27.48-29.36,3.681,3.681,0,0,0,2.253-3.533,3.685,3.685,0,0,0-2.453-3.413,50.468,50.468,0,0,1-29.12-27.707,3.674,3.674,0,0,0-3.52-2.267,3.674,3.674,0,0,0-3.373,2.467,50.549,50.549,0,0,1-27.48,29.373,3.681,3.681,0,0,0-2.253,3.533,3.682,3.682,0,0,0,2.453,3.4,50.51,50.51,0,0,1,29.12,27.706,3.636,3.636,0,0,0,3.52,2.267,3.637,3.637,0,0,0,3.373-2.467" fill="#6759b2" fill-rule="evenodd"/>
|
|
367
|
+
<path id="path258" d="M1573.636-1496.876q-1.82,25.84-28.467,30.413a50.392,50.392,0,0,1,11.707,16.893,3.636,3.636,0,0,0,3.52,2.267,3.637,3.637,0,0,0,3.373-2.467,50.566,50.566,0,0,1,27.48-29.36,3.681,3.681,0,0,0,2.253-3.533,3.685,3.685,0,0,0-2.453-3.413,50.141,50.141,0,0,1-17.413-10.8" fill="#5748a7" fill-rule="evenodd"/>
|
|
368
|
+
<path id="path260" d="M1126.5-1278.943l-4.051,29.8a4.987,4.987,0,0,1-5.6,4.28l-29.56-4.08a5.023,5.023,0,0,1-4.249-5.653l4.049-29.787a5,5,0,0,1,5.6-4.293l29.56,4.093a5.011,5.011,0,0,1,4.249,5.64" fill="#f0393c" fill-rule="evenodd"/>
|
|
369
|
+
<path id="path262" d="M1124.473-1264.05q-19.546,10.44-39.4-5.44l-2.028,14.893a5.022,5.022,0,0,0,4.249,5.653l29.559,4.08a4.984,4.984,0,0,0,5.6-4.28l2.025-14.907" fill="#cb3325" fill-rule="evenodd"/>
|
|
370
|
+
<path id="path264" d="M1534.183-1257.1l21.093,21.267a5.046,5.046,0,0,1,0,7.08l-21.093,21.266a4.957,4.957,0,0,1-7.027,0l-21.107-21.266a5.046,5.046,0,0,1,0-7.08l21.107-21.267a4.934,4.934,0,0,1,7.027,0" fill="#43bb4e" fill-rule="evenodd"/>
|
|
371
|
+
<path id="path266" d="M1544.73-1246.477q-3.58,22-28.12,28.36l10.547,10.627a4.957,4.957,0,0,0,7.027,0l21.093-21.267a5.046,5.046,0,0,0,0-7.08l-10.547-10.64" fill="#32a940" fill-rule="evenodd"/>
|
|
372
|
+
</g>
|
|
373
|
+
</svg>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<!-- Balloons -->
|
|
377
|
+
<div class="balloon balloon-1">🎈</div>
|
|
378
|
+
<div class="balloon balloon-2">🎈</div>
|
|
379
|
+
<div class="balloon balloon-3">🎈</div>
|
|
380
|
+
<div class="balloon balloon-4">🎈</div>
|
|
381
|
+
|
|
382
|
+
<!-- Tropical leaves -->
|
|
383
|
+
<div class="leaf leaf-1">🌿</div>
|
|
384
|
+
<div class="leaf leaf-2">🍃</div>
|
|
385
|
+
<div class="leaf leaf-3">🌿</div>
|
|
386
|
+
<div class="leaf leaf-4">🍃</div>
|
|
387
|
+
|
|
388
|
+
<!-- Stars -->
|
|
389
|
+
<div class="star star-1">⭐</div>
|
|
390
|
+
<div class="star star-2">✨</div>
|
|
391
|
+
<div class="star star-3">⭐</div>
|
|
392
|
+
<div class="star star-4">✨</div>
|
|
393
|
+
|
|
394
|
+
<!-- Header -->
|
|
395
|
+
<div class="header">
|
|
396
|
+
<div class="title">🎉 JOIN US FOR A BIRTHDAY PARTY 🎉</div>
|
|
397
|
+
<div class="child-name"><%= data[:party_details][:person_name] %> is turning <%= data[:party_details][:age] %>!</div>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<!-- Content -->
|
|
401
|
+
<div class="content">
|
|
402
|
+
<% if data[:party_details][:theme] %>
|
|
403
|
+
<div class="info-section theme-highlight">
|
|
404
|
+
<span class="info-label">🎭 PARTY THEME</span>
|
|
405
|
+
<div class="info-value"><%= data[:party_details][:theme] %></div>
|
|
406
|
+
</div>
|
|
407
|
+
<% end %>
|
|
408
|
+
|
|
409
|
+
<div style="display: flex">
|
|
410
|
+
<div class="info-section">
|
|
411
|
+
<span class="info-label">📅 WHEN</span>
|
|
412
|
+
<div class="info-value">
|
|
413
|
+
<%= Date.new(data[:party_details][:date][:year], data[:party_details][:date][:month], data[:party_details][:date][:day]).strftime("%A, %B %d, %Y") %>
|
|
414
|
+
<br>
|
|
415
|
+
<%= data[:party_details][:start_time] %><% if data[:party_details][:end_time] %> - <%= data[:party_details][:end_time] %><% end %>
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
|
|
419
|
+
<div class="info-section">
|
|
420
|
+
<span class="info-label">📍 WHERE</span>
|
|
421
|
+
<div class="info-value">
|
|
422
|
+
<% if data[:venue][:name] %>
|
|
423
|
+
<strong><%= data[:venue][:name] %></strong><br>
|
|
424
|
+
<% end %>
|
|
425
|
+
<%= data[:venue][:address] %>
|
|
426
|
+
<% if data[:venue][:additional_instructions] %>
|
|
427
|
+
<br><em><%= data[:venue][:additional_instructions] %></em>
|
|
428
|
+
<% end %>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
<% if data[:activities] && (data[:activities][:main_activities] || data[:activities][:entertainment]) %>
|
|
434
|
+
<div class="info-section">
|
|
435
|
+
<span class="info-label">🎮 FUN ACTIVITIES</span>
|
|
436
|
+
<div class="info-value">
|
|
437
|
+
<% if data[:activities][:main_activities] %>
|
|
438
|
+
<%= [data[:activities][:main_activities]].flatten.join(" • ") %>
|
|
439
|
+
<% end %>
|
|
440
|
+
<% if data[:activities][:entertainment] %>
|
|
441
|
+
<% if data[:activities][:main_activities] %><br><% end %>
|
|
442
|
+
<strong>Special Guest:</strong> <%= data[:activities][:entertainment] %>
|
|
443
|
+
<% end %>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
<% end %>
|
|
447
|
+
|
|
448
|
+
<div style="display: flex">
|
|
449
|
+
<% if data[:special_instructions] %>
|
|
450
|
+
<% if data[:special_instructions][:dress_code] %>
|
|
451
|
+
<div class="info-section">
|
|
452
|
+
<span class="info-label">👕 DRESS CODE</span>
|
|
453
|
+
<div class="info-value"><%= data[:special_instructions][:dress_code] %></div>
|
|
454
|
+
</div>
|
|
455
|
+
<% end %>
|
|
456
|
+
|
|
457
|
+
<% if data[:special_instructions][:what_to_bring] %>
|
|
458
|
+
<div class="info-section">
|
|
459
|
+
<span class="info-label">🎒 PLEASE BRING</span>
|
|
460
|
+
<div class="info-value"><%= data[:special_instructions][:what_to_bring] %></div>
|
|
461
|
+
</div>
|
|
462
|
+
<% end %>
|
|
463
|
+
<% end %>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
<!-- RSVP Section -->
|
|
468
|
+
<div class="info-section rsvp-section">
|
|
469
|
+
<span class="info-label">💌 RSVP BY <%= Date.new(data[:rsvp][:deadline][:year], data[:rsvp][:deadline][:month], data[:rsvp][:deadline][:day]).strftime("%B %d").upcase %></span>
|
|
470
|
+
<div class="info-value">
|
|
471
|
+
<strong><%= data[:contact_info][:host_name] %></strong><br>
|
|
472
|
+
<% if data[:rsvp][:method].downcase.include?('phone') || data[:rsvp][:method].downcase.include?('text') %>
|
|
473
|
+
📞 <%= data[:contact_info][:phone] %>
|
|
474
|
+
<% end %>
|
|
475
|
+
<% if data[:rsvp][:method].downcase.include?('email') && data[:contact_info][:email] %>
|
|
476
|
+
<% if data[:rsvp][:method].downcase.include?('phone') || data[:rsvp][:method].downcase.include?('text') %><br><% end %>
|
|
477
|
+
📧 <%= data[:contact_info][:email] %>
|
|
478
|
+
<% end %>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
<div class="footer-deco">🌟 Let's have a ROARING good time! 🌟</div>
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
485
|
+
</body>
|
|
486
|
+
</html>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kamisaku
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sinaru Gunawardena
|
|
@@ -25,35 +25,41 @@ files:
|
|
|
25
25
|
- LICENSE.txt
|
|
26
26
|
- README.md
|
|
27
27
|
- Rakefile
|
|
28
|
-
- examples/
|
|
29
|
-
- examples/
|
|
30
|
-
- examples/
|
|
31
|
-
- examples/
|
|
32
|
-
- examples/
|
|
33
|
-
- examples/
|
|
34
|
-
- examples/
|
|
28
|
+
- examples/birthday_invitation/dino/invitation.pdf
|
|
29
|
+
- examples/resume/chromatic/example.pdf
|
|
30
|
+
- examples/resume/gradient/example.pdf
|
|
31
|
+
- examples/resume/meridian/example.pdf
|
|
32
|
+
- examples/resume/paper/example.pdf
|
|
33
|
+
- examples/resume/prism/example.pdf
|
|
34
|
+
- examples/resume/sleek/example.pdf
|
|
35
|
+
- examples/resume/zenith/example.pdf
|
|
35
36
|
- kamisaku.gemspec
|
|
36
37
|
- kamisaku.png
|
|
37
38
|
- lib/kamisaku.rb
|
|
38
39
|
- lib/kamisaku/arg_parser.rb
|
|
39
40
|
- lib/kamisaku/chrome_runner.rb
|
|
40
41
|
- lib/kamisaku/cli_runner.rb
|
|
41
|
-
- lib/kamisaku/
|
|
42
|
+
- lib/kamisaku/content_validators/base_content_validator.rb
|
|
43
|
+
- lib/kamisaku/content_validators/birthday_invitation_content_validator.rb
|
|
44
|
+
- lib/kamisaku/content_validators/resume_content_validator.rb
|
|
42
45
|
- lib/kamisaku/errors.rb
|
|
43
46
|
- lib/kamisaku/helpers.rb
|
|
44
47
|
- lib/kamisaku/html_builder.rb
|
|
45
48
|
- lib/kamisaku/pdf.rb
|
|
46
49
|
- lib/kamisaku/template_helpers.rb
|
|
47
50
|
- lib/kamisaku/version.rb
|
|
48
|
-
- lib/schema/example.yml
|
|
49
|
-
- lib/schema/schema.yml
|
|
50
|
-
- lib/
|
|
51
|
-
- lib/
|
|
52
|
-
- lib/templates/
|
|
53
|
-
- lib/templates/
|
|
54
|
-
- lib/templates/
|
|
55
|
-
- lib/templates/
|
|
56
|
-
- lib/templates/
|
|
51
|
+
- lib/schema/birthday_invitation/example.yml
|
|
52
|
+
- lib/schema/birthday_invitation/schema.yml
|
|
53
|
+
- lib/schema/resume/example.yml
|
|
54
|
+
- lib/schema/resume/schema.yml
|
|
55
|
+
- lib/templates/birthday_invitation/dino/template.html.erb
|
|
56
|
+
- lib/templates/resume/chromatic/template.html.erb
|
|
57
|
+
- lib/templates/resume/gradient/template.html.erb
|
|
58
|
+
- lib/templates/resume/meridian/template.html.erb
|
|
59
|
+
- lib/templates/resume/paper/template.html.erb
|
|
60
|
+
- lib/templates/resume/prism/template.html.erb
|
|
61
|
+
- lib/templates/resume/sleek/template.html.erb
|
|
62
|
+
- lib/templates/resume/zenith/template.html.erb
|
|
57
63
|
- scripts/rebuild_examples.rb
|
|
58
64
|
- sig/kamisaku.rbs
|
|
59
65
|
homepage: https://github.com/sinaru/kamisaku
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|